GNOME Bugzilla – Bug 136898
Build fails when compiling with the option -fdata-sections for gcc
Last modified: 2012-01-31 23:25:06 UTC
Distribution: Debian testing/unstable Package: gnome-games Severity: minor Version: GNOME2.5.91 2.5.x Gnome-Distributor: GARNOME Synopsis: Build fails when compiling with the option -fdata-sections for gcc Bugzilla-Product: gnome-games Bugzilla-Component: aisleriot Bugzilla-Version: 2.5.x Description: aisleriot (gnome-games-2.5.8) does not build successfully on gcc 3.3.3 (Debian GNU/Linux testing/i686) when compiling with the option -fdata-sections. Using this option causes a linker error due to multiply defined symbols. Steps to reproduce the problem: 1. Run the gnome-games-2.5.8 configure script with the argument `CFLAGS=-fdata-sections'. 2. Run make first in the `libgames-support', then in the `gdk-card-image', and finally in the `aisleriot' subdirectory. Actual Results: The compilation of aisleriot fails with the following output: gcc -fdata-sections -o .libs/sol sol.o slot.o dialog.o cscmi.o events.o press_data.o draw.o menu.o card.o statistics.o -Wl,--export-dynamic -pthread -L/opt/garnome/lib -L/usr/X11R6/lib ../gdk-card-image/.libs/libgdkcardimage.so /opt/garnome/lib/libglade-2.0.so /opt/garnome/lib/libgnomeui-2.so /opt/garnome/lib/libgnome-keyring.so /usr/lib/libjpeg.so /opt/garnome/lib/libbonoboui-2.so /opt/garnome/lib/libXcursor.so /opt/garnome/lib/libXrender.so -lSM -lICE -lX11 /opt/garnome/lib/libgnomecanvas-2.so /opt/garnome/lib/libgnome-2.so /opt/garnome/lib/libesd.so /opt/garnome/lib/libaudiofile.so /opt/garnome/lib/libart_lgpl_2.so /opt/garnome/lib/libpangoft2-1.0.so /opt/garnome/lib/libgnomevfs-2.so /opt/garnome/lib/libgconf-2.so /opt/garnome/lib/libbonobo-2.so /opt/garnome/lib/libxml2.so -lz -lssl -lcrypto -lrt /opt/garnome/lib/libbonobo-activation.so /opt/garnome/lib/libORBitCosNaming-2.so /opt/garnome/lib/libORBit-2.so /usr/lib/libpopt.so /opt/garnome/lib/libgthread-2.0.so /opt/garnome/lib/libgtk-x11-2.0.so /opt/garnome/lib/libgdk-x11-2.0.so /opt/garnome/lib/libatk-1.0.so /opt/garnome/lib/libgdk_pixbuf-2.0.so /opt/garnome/lib/libpangoxft-1.0.so /opt/garnome/lib/libpangox-1.0.so /opt/garnome/lib/libpango-1.0.so /opt/garnome/lib/libgobject-2.0.so /opt/garnome/lib/libgmodule-2.0.so /opt/garnome/lib/libglib-2.0.so ../libgames-support/.libs/libgames-support.a -L/usr/lib /usr/lib/libguile.so /usr/lib/libltdl.so -ldl /usr/lib/libqthreads.so -lpthread -lm -Wl,--rpath -Wl,/usr/local/lib -Wl,--rpath -Wl,/opt/garnome/lib press_data.o(.bss.press_data+0x0): multiple definition of `press_data' sol.o(.bss.press_data+0x0): first defined here collect2: ld returned 1 exit status make[1]: *** [sol] Error 1 make[1]: Leaving directory `/tmp/gnome-games-2.5.8/aisleriot' make: *** [all-recursive] Error 1 Additional information: `press_data' is defined in the source files `aisleriot/sol.c' and `aisleriot/press_data.c'. Changing either of these definitions to an extern declaration allows aisleriot to build successfully. ------- Bug moved to this database by unknown@bugzilla.gnome.org 2004-03-11 10:38 ------- The original reporter (heikki.tauriainen@hut.fi) of this bug does not have an account here. Reassigning to the exporter, unknown@bugzilla.gnome.org. Reassigning to the default owner of the component, zana@webwynk.net.
This looks like gcc's problem, not mine. Variables declared outside of any function should have external linkage by default so the extern modifier is optional (albeit good practice). gcc appears to be generating the section names directly from the variable names and getting a conflict. With the extern modifier it appears to be able to resolve this. We are in code freeze for 2.6 so nothing is going to change before then. For now don't use -fdata-sections. The code that is triggering this is definitely less than optimal and I will leave this bug open so that I go back and fix it. These comments apply to this and your previous two bugs, but this is the only one I've looked at properly.
Yes, identifiers defined outside of functions have external linkage by default. However, the extern modifier doesn't simply restate here that an identifier should have external linkage; instead, it works as a storage-class specifier (i.e., it effectively prevents the compiler to reserve storage for the variable in two separate object files). What I believe happens here is that declaring `press_data' in two separate translation units without a storage-class specifier effectively causes the variable to have two external definitions in the entire program, which isn't allowed in strict C. (Because the declarations have no initializers, leaving out the extern modifier makes both declarations tentative definitions in two separate translation units. This causes the compiler to reserve storage for the variable in two separate object files.) Actually, the reason why the compilation succeeds when not using -fdata-sections seems to be that in this case `press_data' will be a "common" symbol in both of the object files, and the (GNU) linker is able to combine these symbols into one when linking. Apparently -fdata-sections has the side effect of disabling common symbols (when compiling with this option, `press_data' ends up in the uninitialized data section in both object files, and this may be what then causes the linker error). Indeed, the same error occurs when compiling with the option -fno-common instead of -fdata-sections. Anyway, the reason why I decided to report this bug (together with bugs #136897 and #136896) in the first place was simply to point out a (theoretical) possibility of a similar build problem on platforms with less permissive compilers and linkers (on Linux, there is the trivial "don't do that" workaround, of course). Sorry for not being clear enough in my bug report.
I admit that the only reference I have for the C language at hand is O'Reilly's C Pocket Reference which hardly goes into any detail but seemed to imply that this would be OK. Since at least the press_data example has been present for some time with no complaints from people on "exotic" systems I'm not too worried. I've heard of recent successes on Linux, FreeBSD and Solaris so I'm reasonably happy. I am going to fix this in 2.7 (i.e. CVS HEAD) though since it is bad C (regardless of whether it is legal C), but not 2.6 since we are only a week away from release and I can't afford to accidentally break anything (I've already caused at least one bug with a single-digit change!).
That's ok. Thanks for the information.
It is now fixed in my local copy. press_data is pretty evil anyway, but now the evil is only spread across two files. I'll upload it to CVS soonish, but I've only just branched and I have to let people realise this before I start making gratuitous changes.
Since I needed to know the real answer. From the C standard (http://std.dkuug.dk/JTC1/SC22/WG14/www/docs/n843.htm, actually it is the final draft): Section 6.2.2#5: If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external. Section 6.2.2#2: In the set of translation units and libraries that constitutes an entire program, each declaration of a particular identifier with external linkage denotes the same object or function. I think that answers the question. This is of course the same behaviour we expect with function names.
I understand that your conclusion is that the code _is_ standard-conforming even without storage-class specifiers? (If I misunderstood this, just forget about the rest of this comment.) It's funny that I came to the opposite conclusion by looking at the same draft of the standard (sorry to continue this debate, but the question whether the code without the storage-class specifiers is actually correct C got really interesting :-) ): External definitions are discussed in Section 6.9 of the draft. Section 6.9.2 (External object definitions): [#1] If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external definition for the identifier. [#2] A declaration of an identifier for an object that has file scope without an initializer, and without a storage- class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0. In this particular case, it follows from #2 that the declaration `press_data_type* press_data;' in `sol.c' and `press_data.c' is a tentative definition for the variable in both files. Because there are no explicit external definitions (in the sense of #1) for the variable in the files, the tentative definitions are then implicitly treated as if they'd both been written in the form `press_data_type* press_data = 0;' (by #2). Therefore both declarations are effectively external definitions by #1 (and have external linkage). The fact that `sol.c' and `press_data.c' don't belong to the same translation unit is now what I believe what makes the original code violate the C standard, since in Section 6.9 (External definitions) it's stated that a program may contain at most one external definition for each object with external linkage: [#5] An external definition is an external declaration that is also a definition of a function or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof operator), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one. BTW, I noticed that `sol.h' already contains a declaration for `press_data' (with the `extern' storage-class specifier), so maybe the definition in `sol.c' isn't even needed.
I agree with the first points, but I think you are misinterpreting 6.9#5. There is indeed only one *definition* of an external *declaration*, but there can be multiple declarations that must have identical definitions (up to the fuzziness about initialisation and int[] vs. int[10]). If you interpreted things your way then you could never have "extern int a" in one file (an explicit external declaration) and "int a" in another file (an implicit external declaration) since this would be two external declarations. However they are the same external definition (i.e. both int). Having "extern int a" in one file and "double a" in another is what I think 6.9 si trying to avoid. Also note that 6.9#5 does not distinguish functions and objects. Declaring functions without extern and then declaring it with a prototype (also without extern) in another file is a very widly accepted practice and is exactly what was done in aisleriot, just with an object instead. Damn, we need a language lawyer. On the last point about where press_data is defined: you are absolutely correct, that is how I fixed it.
I absolutely agree about the need for a language lawyer here :-) I think we may be having different views about the exact meaning of declarations and definitions; I use the semantics defined in Section 6.7#5 of the standard draft: [#5] A declaration specifies the interpretation and attributes of a set of identifiers. A definition of an identifier is a declaration for that identifier that: -- for an object, causes storage to be reserved for that object; -- for a function, includes the function body; -- for an enumeration constant or typedef name, is the (only) declaration of the identifier. It's still perfectly ok to have `extern int a' in one file and `int a' in another even if my interpretation is correct. Here, `int a' is a declaration that's also a (tentative) definition for the variable (by 6.9.2#2), whereas `extern int a' is a declaration that reserves no storage (by 6.9.2#2, the `extern' specifier prevents the compiler from treating the declaration as another tentative definition; removing it would make `a' have two definitions in the entire program, which is forbidden by 6.9#5). It's confusing that the `extern' specifier is indeed redundant with function declarations (by 6.7#5, a function declaration is a definition only if it contains also the function body). However, the same doesn't hold for objects that are not functions. (For another description about the difference, see Example 4.9 at <http://publications.gbdirect.co.uk/c_book/chapter4/linkage.html>. Although the book there isn't based on the latest C standard, I think the explanation is still valid.)
Heiki is correct here. See http://std.dkuug.dk/JTC1/SC22/WG14/www/C99RationaleV5.10.pdf, section 6.2.2.
OK, the rational explains things. I don't want to adjust 2.6 at this late stage, although I'll probably back-port the changes to 2.6.1 once HEAD has had a chance to show up problems. I have to say that the standard isn't exactly clear and the rational isn't much better. Now I get to add -fdata-sections to my list of useful error-finding techniques !
This bug is being reassigned to the "general" component so we can close the aisleriot bugzilla component. Apologies for the mass email!