Tuesday February 08, 2005
C++, name mangling and function signatures
One of the guys in the office was having problems compiling POVray on solaris - the last link step kept failing with an unknown symbol. It turns out that the POVray source had declared the symbol in the .h file using the pattern:
int function(const char *, unsigned int);but defined the function in the .cpp file using the pattern:
int function(const char *, const unsigned int) {}
When it came to the link step, the compiler complained that other object files referencing the function could not link, because the function as declared was not in any of the linked object files. We can easily ascertain this fact using the nm utility. To more easily see the differences, you need to use the -C option, which performs the neat-oh C++ name demangling.The example code is here. Which demonstrates this issue precisely.
First we compile, but do not link the .cpp files. To perform this step do: CC -c *.cpp.
% nm -C *.o
inter.o:
[Index] Value Size Type Bind Other Shndx Name
[2] | 0| 25|FUNC |GLOB |0 |2 |int function(const char*,const unsigned)
[__1cIfunction6FpkckI_i_]
[1] | 0| 0|FILE |LOCL |0 |ABS |inter.cpp
link.o:
[Index] Value Size Type Bind Other Shndx Name
[4] | 0| 0|NOTY |GLOB |0 |ABS |__fsr_init_value
[2] | 0| 0|FUNC |GLOB |0 |UNDEF |int function(const char*,unsigned)
[__1cIfunction6FpkcI_i_]
[1] | 0| 0|FILE |LOCL |0 |ABS |link.cpp
[3] | 0| 41|FUNC |GLOB |0 |2 |main
without the demangling, you would see the function names in brackets, which are not easy to distinguish, but once you add in the demangling the difference is obvious. Of course even if you #included the .h file into the .cpp file to 'type enforce' the function signature, because to the compiler they are not the same function it simply presumes that the declaration in the .h file is for another function.It is annoying, but you have to pay attention to these things when you're writing C++.
Contrast the patterns observed when we compile using the gnu compiler: g++ -c *.cpp
% nm -C *.o
inter.o:
[Index] Value Size Type Bind Other Shndx Name
[2] | 0| 0|SECT |LOCL |0 |1 |
[3] | 0| 0|SECT |LOCL |0 |2 |
[5] | 0| 0|SECT |LOCL |0 |4 |
[4] | 0| 0|SECT |LOCL |0 |3 |
[6] | 0| 10|FUNC |GLOB |0 |1 |function(const char*, unsigned)
[_Z8functionPKcj]
[1] | 0| 0|FILE |LOCL |0 |ABS |inter.cpp
link.o:
[Index] Value Size Type Bind Other Shndx Name
[5] | 0| 0|SECT |LOCL |0 |5 |
[2] | 0| 0|SECT |LOCL |0 |1 |
[3] | 0| 0|SECT |LOCL |0 |3 |
[4] | 0| 0|SECT |LOCL |0 |4 |
[6] | 0| 0|SECT |LOCL |0 |7 |
[9] | 0| 0|NOTY |GLOB |0 |UNDEF |__gxx_personality_v0
[8] | 0| 0|NOTY |GLOB |0 |UNDEF |function(const char*, unsigned)
[_Z8functionPKcj]
[1] | 0| 0|FILE |LOCL |0 |ABS |link.cpp
[7] | 0| 53|FUNC |GLOB |0 |1 |main
Who is correct at this step? I don't quite have the answer for that, as if you tried to define two functions that differed by that const, the compiler has no way to disambiguate them. If you're being really picky, you could say that dropping the const against standard pass-by-value variables is incorrect; after all it's a function taking a const, and it's declaration should respect that.
February 08, 2005 01:09 PM GMT
Permalink