Bug 56066 - g++ generates strong symbols conflicting with C99 extern inline code on Windows
Summary: g++ generates strong symbols conflicting with C99 extern inline code on Windows
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 4.7.2
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-01-21 15:04 UTC by Solomon Gibbs
Modified: 2021-06-20 09:23 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2013-11-26 00:00:00


Attachments
Header file, C99 and C++ file that create conflict (2.78 KB, application/zip)
2013-01-21 15:04 UTC, Solomon Gibbs
Details
Shared header, C99 and C++ units. With workaround. (7.93 KB, application/x-zip-compressed)
2013-12-31 04:40 UTC, Solomon Gibbs
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Solomon Gibbs 2013-01-21 15:04:47 UTC
Created attachment 29236 [details]
Header file, C99 and C++ file that create conflict

When a C99 translation unit containing extern inline functions is linked with a C++ translation unit using those same inlines, g++ generates an additional symbol for the inline function in the C++ TU. Linking causes a multiple definitions error.

I am informed that this is a likely a problem with PE/COFF and that under ELF, the g++ generated symbols would be marked weak and eliminated at link time. 

For example, when buffer.h as shown below is included by C99 and C++ TUs, the error occurs. (Complete files are attached)

// buffer.h
inline bool has_remaining(void* obj) {
...
}

// buffer.c
extern inline bool has_remaining(void* obj);

// app.cpp
int main(...) {
   has_remaining("okay");
   ...
}

// compile
$ gcc -std=gnu99 -o buffer.o -c --save-temps buffer.c
$ g++ -std=gnu++11 -o app.o -c --save-temps app.cpp
$ g++ -Wl,--subsystem,console -o app.exe app.o buffer.o
buffer.o:buffer.c:(.text+0x0): multiple definition of `has_remaining'
app.o:app.cpp:(.text$has_remaining[_has_remaining]+0x0): first defined here
collect2.exe: error: ld returned 1 exit status


$ gcc -v
Using built-in specs.
COLLECT_GCC=C:\opt\MinGW\bin\gcc.exe
COLLECT_LTO_WRAPPER=c:/opt/mingw/bin/../libexec/gcc/mingw32/4.7.2/lto-wrapper.exe
Target: mingw32
Configured with: ../gcc-4.7.2/configure --enable-languages=c,c++,ada,fortran,objc,obj-c++ --disable-sjlj-exceptions --with-dwarf2 --enable-shared --enable-libgomp --disable-win32-registry --enable-libstdcxx-debug --disable-build-poststage1-with-cxx --enable-version-specific-runtime-libs --build=mingw32 --prefix=/mingw
Thread model: win32
gcc version 4.7.2 (GCC)


$ g++ -v
Using built-in specs.
COLLECT_GCC=C:\opt\MinGW\bin\g++.exe
COLLECT_LTO_WRAPPER=c:/opt/mingw/bin/../libexec/gcc/mingw32/4.7.2/lto-wrapper.exe
Target: mingw32
Configured with: ../gcc-4.7.2/configure --enable-languages=c,c++,ada,fortran,objc,obj-c++ --disable-sjlj-exceptions --with-dwarf2 --enable-shared --enable-libgomp --disable-win32-registry --enable-libstdcxx-debug --disable-build-poststage1-with-cxx --enable-version-specific-runtime-libs --build=mingw32 --prefix=/mingw
Thread model: win32
gcc version 4.7.2 (GCC)


$ ld -v
GNU ld (GNU Binutils) 2.22
Comment 1 Solomon Gibbs 2013-01-24 19:42:15 UTC
I'm looking at the objdump -x output for the c++ object and I note that there's a separate section for the inlined function. It appears to be annotated with a COMDAT field value of 4. According to skyfree.org/linux/references/coff.pdf a value of 4 corresponds to IMAGE_COMDAT_SELECT_EXACT_MATCH: "The linker chooses an arbitrary section among the definitions for this symbol. A multiply defined symbol error is issued if all definitions don’t match exactly." The C99 symbol has no COMDAT selection field.
Comment 2 Kai Tietz 2013-11-26 18:27:23 UTC
I can't reproduce your problem with 4.7.  At least not with given sample.

Could you please provide more detailed, short, and compilable testcase.
Comment 3 Kai Tietz 2013-12-27 14:02:44 UTC
No reply.  So I assume bug was fixed already.  Please don't hesitate to reopen this bug (with small testcase for reproducing it) again, if it re-appears.
Comment 4 Solomon Gibbs 2013-12-31 04:40:20 UTC
Created attachment 31545 [details]
Shared header, C99 and C++ units. With workaround.
Comment 5 Solomon Gibbs 2013-12-31 05:02:26 UTC
Sorry, I missed the request for more information.

This is still broken for me under 4.8.1. 

In a nutshell, I think that differing assumptions about inline functions made by g++ and gcc are carrying over into object file output and preventing linking. 

Working from a shared header with a non-static inline function, gcc explicitly creates an implementation at the location of an "extern inline ...". On the other hand, g++ creates an implementation in every unit where the header was included, intending to throw away redundant definitions at link time. 

This gives us a definition in the C unit and the C++ unit. However, for some reason, possibly related to the difference in COFF data, the linker is unable to determine that the definitions are truly identical as it would if they both came from a non-static C++ inline.

One workaround is to prevent the C++ definition from being created using the gnu_inline function attribute.


I updated the original attachment to show the workaround and added a Makefile. Since this requires a shared header, C and C++ units to manifest, I did not paste all three files into the comment, but left them in the attachment archive. Please let me know if you still need something else to reproduce the issue.

The relevant output is obtained from objdump as:

$ objdump -x app-broken.o |grep -B1 -e 'COMDAT _problem_function'
  3 .text$problem_function 0000000c  00000000  00000000  00000180  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE, LINK_ONCE_DISCARD (COMDAT _problem_function 4)


$ gcc -v
Using built-in specs.
COLLECT_GCC=C:\opt\MinGW-w64\x86-posix-dwarf\bin\gcc.exe
COLLECT_LTO_WRAPPER=c:/opt/mingw-w64/x86-posix-dwarf/bin/../libexec/gcc/i686-w64-mingw32/4.8.1/lto-wrapper.exe
Target: i686-w64-mingw32
Configured with: ../../../src/gcc-4.8.1/configure --host=i686-w64-mingw32 --build=i686-w64-mingw32 --target=i686-w64-mingw32 --prefix=/mingw32 --with-sysroot=/tmp/x32-481-posix-dwarf-r5/mingw32 --enable-shared --enable-static --disable-multilib --enable-languages=ada,c,c++,fortran,objc,obj-c++,lto --enable-libstdcxx-time=yes --enable-threads=posix --enable-libgomp --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --disable-sjlj-exceptions --with-dwarf2 --disable-isl-version-check --disable-cloog-version-check --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=i686 --with-tune=generic --with-libiconv --with-system-zlib --with-gmp=/tmp/mingw-prereq/i686-w64-mingw32-static --with-mpfr=/tmp/mingw-prereq/i686-w64-mingw32-static --with-mpc=/tmp/mingw-prereq/i686-w64-mingw32-static --with-isl=/tmp/mingw-prereq/i686-w64-mingw32-static --with-cloog=/tmp/mingw-prereq/i686-w64-mingw32-static --enable-cloog-backend=isl --with-pkgversion='rev5, Built by MinGW-W64 project' --with-bugurl=http://sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -I/tmp/x32-481-posix-dwarf-r5/libs/include -I/tmp/mingw-prereq/x32-zlib/include -I/tmp/mingw-prereq/i686-w64-mingw32-static/include' CXXFLAGS='-O2 -pipe -I/tmp/x32-481-posix-dwarf-r5/libs/include -I/tmp/mingw-prereq/x32-zlib/include -I/tmp/mingw-prereq/i686-w64-mingw32-static/include' CPPFLAGS= LDFLAGS='-pipe -L/tmp/x32-481-posix-dwarf-r5/libs/lib -L/tmp/mingw-prereq/x32-zlib/lib -L/tmp/mingw-prereq/i686-w64-mingw32-static/lib -L/tmp/x32-481-posix-dwarf-r5/mingw32/opt/lib -Wl,--large-address-aware'
Thread model: posix
gcc version 4.8.1 (rev5, Built by MinGW-W64 project)


$ g++ -v
Using built-in specs.
COLLECT_GCC=C:\opt\MinGW-w64\x86-posix-dwarf\bin\g++.exe
COLLECT_LTO_WRAPPER=c:/opt/mingw-w64/x86-posix-dwarf/bin/../libexec/gcc/i686-w64-mingw32/4.8.1/lto-wrapper.exe
Target: i686-w64-mingw32
Configured with: ../../../src/gcc-4.8.1/configure --host=i686-w64-mingw32 --build=i686-w64-mingw32 --target=i686-w64-mingw32 --prefix=/mingw32 --with-sysroot=/tmp/x32-481-posix-dwarf-r5/mingw32 --enable-shared --enable-static --disable-multilib --enable-languages=ada,c,c++,fortran,objc,obj-c++,lto --enable-libstdcxx-time=yes --enable-threads=posix --enable-libgomp --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --disable-sjlj-exceptions --with-dwarf2 --disable-isl-version-check --disable-cloog-version-check --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=i686 --with-tune=generic --with-libiconv --with-system-zlib --with-gmp=/tmp/mingw-prereq/i686-w64-mingw32-static --with-mpfr=/tmp/mingw-prereq/i686-w64-mingw32-static --with-mpc=/tmp/mingw-prereq/i686-w64-mingw32-static --with-isl=/tmp/mingw-prereq/i686-w64-mingw32-static --with-cloog=/tmp/mingw-prereq/i686-w64-mingw32-static --enable-cloog-backend=isl --with-pkgversion='rev5, Built by MinGW-W64 project' --with-bugurl=http://sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -I/tmp/x32-481-posix-dwarf-r5/libs/include -I/tmp/mingw-prereq/x32-zlib/include -I/tmp/mingw-prereq/i686-w64-mingw32-static/include' CXXFLAGS='-O2 -pipe -I/tmp/x32-481-posix-dwarf-r5/libs/include -I/tmp/mingw-prereq/x32-zlib/include -I/tmp/mingw-prereq/i686-w64-mingw32-static/include' CPPFLAGS= LDFLAGS='-pipe -L/tmp/x32-481-posix-dwarf-r5/libs/lib -L/tmp/mingw-prereq/x32-zlib/lib -L/tmp/mingw-prereq/i686-w64-mingw32-static/lib -L/tmp/x32-481-posix-dwarf-r5/mingw32/opt/lib -Wl,--large-address-aware'
Thread model: posix
gcc version 4.8.1 (rev5, Built by MinGW-W64 project)
Comment 6 Andrew Pinski 2021-06-20 09:23:01 UTC
I don't think this is a bug.  Rather the problem is you are linking two different linkages and expecting it to work. in the C++ case, it is vague linkage while in C, there is extern linkage still.
the correct thing is to use gnu_inline so it is the linkage you expect in both langauges.