PROBLEM: compilation issue, Incorrect C usage in drivers/block/null_blk.c causes kernel compilation failure with Intel c++ compiler

From: Blower, Melanie
Date: Wed Jan 13 2016 - 14:23:27 EST

[1.] Incorrect C usage in drivers/block/null_blk.c causes kernel compilation failure with Intel c++ compiler
[2.] Full description of the problem/report:
Using icc,
drivers/block/null_blk.c(569): error: variable "null_lnvm_dev_ops" was declared with a never-completed type
static struct nvm_dev_ops null_lnvm_dev_ops;

Clark Nelson, one of Intel's C++ language lawyers, explains why this declaration is illegal:

Here is the problematic declaration, which appears near line 585 of file drivers/block/null_blk.c:

static struct nvm_dev_ops null_lnvm_dev_ops;

This clearly violates 6.9.2 paragraph 3 of the C standard:

If the declaration of an identifier for an object is a tentative definition and has internal linkage, the declared type shall not be an incomplete type.

The declaration is a tentative definition because it has no initializer, it has internal linkage because the static keyword is used, and yet the type is definitely an incomplete type.

In general, the problem with such a declaration is that the compiler is expected to allocate memory for it in some data section, but the compiler is given no idea how much memory to allocate.

As it turns out, the only way this variable is used is to take its address and pass it to an inline function, which doesn't use the corresponding pointer parameter at all. So after inlining and dead code elimination, no reference to the variable survives, so it doesn't need to be allocated after all, so it doesn't matter that the size of the allocation isn't known.

When optimization isn't used, and the variable isn't discovered to be unnecessary, GCC also reports an error for this declaration.

However, it's fairly obvious that GCC wasn't carefully and consistently designed to allow this sort of thing. If the same declaration appears in a block scope, without the static keyword, GCC reports an error regardless of the optimization level, even if the variable is never referenced at all, even if the declaration appears in a static function that is never used. The logic by which the original static declaration is accepted would suggest that the auto declaration should also be accepted, but it isn't.

The simple fix would be to add an empty member list to the declaration:

static struct nvm_dev_ops {} null_lnvm_dev_ops;

That would still not conform to the C standard, which doesn't allow an empty member list. But support for an empty member list is much more stable and predictable than support for optimizing out unused invalid variable declarations.

[3.] Keywords : kernel
[4.] Kernel version (from /proc/version):
We're building linux-4.4-rc8.tar.xz with the Intel compiler
Here's host information where the Kernel build occurs:
Linux version 3.10.0-229.el7.x86_64 (mockbuild@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) (gcc version 4.8.3 20140911 (Red Hat 4.8.3-7) (GCC) ) #1 SMP Thu Jan 29 18:37:38 EST 2015
[5.] Output of Oops.. message (if applicable) with symbolic information
resolved (see Documentation/oops-tracing.txt)
[6.] A small shell script or example program which triggers the
problem (if possible)
Compile kernel linux-4.4-rc8.tar.xz with Intel C++ compiler for Linux version 16.0 update 1
[7.] Environment
I'm not going to include the environment details since they're irrelevant.
[7.1.] Software (add the output of the ver_linux script here)
[7.2.] Processor information (from /proc/cpuinfo):
[7.3.] Module information (from /proc/modules):
[7.4.] Loaded driver and hardware information (/proc/ioports, /proc/iomem)
[7.5.] PCI information ('lspci -vvv' as root)
[7.6.] SCSI information (from /proc/scsi/scsi)
[7.7.] Other information that might be relevant to the problem
(please look in /proc and include all information that you
think to be relevant):
[X.] Other notes, patches, fixes, workarounds:

Please cc: me on replies to this message. Thanks and regards, Melanie Blower (Intel C++ compiler team)