Why does g ++ linker not warn about this inconsistent function declaration?

This has been tested for Debian compression with g ++ 4.4 and g ++ 4.7. Consider the two C ++ source files.

################ foo.cc ################# #include <string> using std::string; int foo(void) { return 0; } ################# bar.cc ################# #include <string> using std::string; //int foo(void); string foo(void); int main(void) { foo(); return 0; } ################## 

If I compile and run it, problems predictably arise. I use scons.

 ################################ SConstruct ################################ #!/usr/bin/python env = Environment( CXX="g++-4.7", CXXFLAGS="-Wall -Werror", #CXX="g++", #CXXFLAGS="-Wall -Werror", ) env.Program(target='debug', source=["foo.cc", "bar.cc"]) ################################# 

Compilation and launch ...

 $ scons g++-4.7 -o bar.o -c -Wall -Werror bar.cc g++-4.7 -o foo.o -c -Wall -Werror foo.cc g++-4.7 -o debug foo.o bar.o $ ./debug *** glibc detected *** ./debug: free(): invalid pointer: 0xbff53b8c *** ======= Backtrace: ========= /lib/i686/cmov/libc.so.6(+0x6b381)[0xb7684381] /lib/i686/cmov/libc.so.6(+0x6cbd8)[0xb7685bd8] /lib/i686/cmov/libc.so.6(cfree+0x6d)[0xb7688cbd] /usr/lib/libstdc++.so.6(_ZdlPv+0x1f)[0xb7856c5f] /lib/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xb762fca6] ./debug[0x8048461] ======= Memory map: ======== 08048000-08049000 r-xp 00000000 fd:10 7602195 /home/faheem/corrmodel/linker/debug 08049000-0804a000 rw-p 00000000 fd:10 7602195 /home/faheem/corrmodel/linker/debug 09ae0000-09b01000 rw-p 00000000 00:00 0 [heap] b7617000-b7619000 rw-p 00000000 00:00 0 b7619000-b7759000 r-xp 00000000 fd:00 1180005 /lib/i686/cmov/libc-2.11.3.so b7759000-b775a000 ---p 00140000 fd:00 1180005 /lib/i686/cmov/libc-2.11.3.so b775a000-b775c000 r--p 00140000 fd:00 1180005 /lib/i686/cmov/libc-2.11.3.so b775c000-b775d000 rw-p 00142000 fd:00 1180005 /lib/i686/cmov/libc-2.11.3.so b775d000-b7760000 rw-p 00000000 00:00 0 b7760000-b777c000 r-xp 00000000 fd:00 4653173 /lib/libgcc_s.so.1 b777c000-b777d000 rw-p 0001c000 fd:00 4653173 /lib/libgcc_s.so.1 b777d000-b777e000 rw-p 00000000 00:00 0 b777e000-b77a2000 r-xp 00000000 fd:00 1179967 /lib/i686/cmov/libm-2.11.3.so b77a2000-b77a3000 r--p 00023000 fd:00 1179967 /lib/i686/cmov/libm-2.11.3.so b77a3000-b77a4000 rw-p 00024000 fd:00 1179967 /lib/i686/cmov/libm-2.11.3.so b77a4000-b7889000 r-xp 00000000 fd:00 2484736 /usr/lib/libstdc++.so.6.0.17 b7889000-b788d000 r--p 000e4000 fd:00 2484736 /usr/lib/libstdc++.so.6.0.17 b788d000-b788e000 rw-p 000e8000 fd:00 2484736 /usr/lib/libstdc++.so.6.0.17 b788e000-b7895000 rw-p 00000000 00:00 0 b78ba000-b78bc000 rw-p 00000000 00:00 0 b78bc000-b78bd000 r-xp 00000000 00:00 0 [vdso] b78bd000-b78d8000 r-xp 00000000 fd:00 639026 /lib/ld-2.11.3.so b78d8000-b78d9000 r--p 0001b000 fd:00 639026 /lib/ld-2.11.3.so b78d9000-b78da000 rw-p 0001c000 fd:00 639026 /lib/ld-2.11.3.so bff41000-bff56000 rw-p 00000000 00:00 0 [stack] Aborted 

Eww. This can be avoided if the linker warned that foo declared in two different ways. Even with -Wall this is not the case. So, is there a reason why this is not happening, and is there some kind of flag that I can turn on to alert it? Thanks in advance.

EDIT: Thanks for all the answers. The linker gives a warning when there are conflicting definitions of functions, in contrast to the definition and declaration of conflicting functions, as in my example above. I do not understand the reason for this different behavior.

+4
source share
4 answers

A component simply acts on names that, according to the compiler, are defined in modules, refer (or are required) to modules. GCC seems to use "Itanium C ++ ABI" for management function names (starting with GCC 3). For most functions, the return type is not included in the changed name, so why the linker does not take it into account:

Itanium C ++ ABI

Function types consist of their parameter types and, possibly, the result type. Except for the outer level of type a or in another restricted external name in or function, these types are separated by a pair of "F..E". For replacement purposes (see Compression below), limited and unsupported function types are considered the same.

Whether manipulating a function type with a return type depends on the context and nature of the function. Rules for deciding whether a return type is included:

  • Template functions (names or types) have encoded return types with the exceptions listed below.
  • Types of functions that are not displayed as part of the function name, for example. parameters, pointer types, etc., have a return type with the exceptions listed below.
  • Type names without patterns do not have encoded return types.

The exceptions referred to in points (1) and (2) above, for which return type is never included,

  • Constructors.
  • destructors.
  • Transformation operator functions, for example. int statement

In general, in C ++, the return type of a function is not taken into account when the compiler searches for a name (for example, to allow overloading). This may be part of the reason why the return type is usually not included in the name mangling. I don't know if there is a stronger reason not to include the return type in the malformed name.

+2
source

The C ++ ruler only identifies functions as far as necessary for unique identification.

This is from the next in-depth article on the C ++ linker.

... symbol names are decorated with additional lines. This is called a name change.

Decorating in front of an identifier name is necessary because C ++ supports namespaces. For example, the same function name may occur several times in different namespaces, denoting a different entity each time. To allow the linker to distinguish between these entities, the name of each identifier is added using tokens representing its spanning namespaces.

Decorating after an identifier name is necessary because C ++ allows function overloading. Again, the same function name can mean different identifiers that differ only in their list of parameters. to allow the linker to distinguish between those tokens representing a list of parameters added to the identifier name. the return type of the function is not taken into account, since two overloaded functions should not differ only in their return type.

So the point is that name manipulation applied to functions ignores the type of return value, since overloaded functions cannot differ in return type. Thus, the linker cannot determine the problem.

+4
source

This is the best example of the reason for having a local project header file (possibly foobar.h ), which includes all such functions. Thus, the compiler can see such problems.

Linkers were never going to identify such a problem. Must leave something for real engineers and traders; do.: -)

+2
source
 $ cat foo.cpp #include <string> using std::string; int foo(void) { return 0; } $ cat bar.cpp #include <string> using std::string; //int foo(void); string foo(void); int main(void) { foo(); return 0; } $ g++ -c -o bar.o bar.cpp $ g++ -c -o foo.o foo.cpp $ g++ foo.o bar.o $ ./a.out $ echo $? 0 $ g++ --version g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 

Unable to play.

0
source

Source: https://habr.com/ru/post/1393870/


All Articles