After building my new computer, I’ve been going through all of my projects on gregstoll.com and making sure they work. I try to do this periodically anyway, and moving to a new machine broke some of them.
So I’ve been working on my Pretty Pictures with Genetic Algorithms project, which uses a C++ program on the backend to generate the images. It used to use tinyjson to parse the description of the picture, but that depends on some Boost stuff which was hard to install, so I decided to change to use rapidjson which has no external dependencies. Turns out my C++ is fairly rusty, but after an hour or so I was able to fix all the compile errors!
And then I started hitting linker errors, like:
writepng.cpp:(.text.startup+0x444): undefined reference to `png_create_write_struct'
writepng.cpp:(.text.startup+0x45c): undefined reference to `png_create_info_struct'
“OK”, I thought, I know what this means!
writepng.cpp calls into libpng, and I’m including the header file correctly, but for some reason I’m not linking against
libpng.so. I still remember some things!
This was kind of weird, because I hadn’t actually changed anything about the png code, but it’s a new machine so anything is possible. So I make sure libpng.a is installed and seems to be in a reasonable place.
Then I made sure the linker was finding it by specifying the directory with
-L – same result. (although I later noticed if the linker doesn’t find a library you specify with
-l, you get a different sort of error) Now I’m pretty confused, so I add
-v to get more verbose info from g++, but nothing’s particularly useful.
Right now my command line is:
g++ -v -O2 -Wall -lpng -o writepng writepng.cpp
and I’m still confused. I’m wondering whether this is a name-mangling thing – if
writepng.cpp is expecting the png functions to be C-style (as it appears to, since the missing symbols are just the name of the functions), but they’re actually exported as C++ functions, then this is the sort of error you’d get. So I use
readelf to look at the symbols in
writepng.cpp and they’re all looking for the plain C versions.
I’m basically out of ideas at this point, so I go to bed, and the next day try first compiling the
.cpp into a
.o file and then passing that to g++, which doesn’t work. Now I’m reduced to some frantic searching. After another hour or so, I read a page that implies that the order of arguments to g++ matters, so I try switching around the command to:
g++ -v -O2 -Wall -o writepng writepng.cpp -lpng
-lpng moved to the end) – and it works! The reason is that apparently g++ and gcc evaluate the
-l arguments in order and get rid of any symbols that don’t seem needed at that point. So when
-lpng is first, g++ reads the library, sees a bunch of exported symbols and says “huh, nothing needs these yet so I guess I’ll forget about them”, and only after looking at the
.cpp argument realizes that those symbols are undefined.
I would humbly submit that this is incredibly user-hostile. People don’t expect the order of arguments to a command-line program to make a difference! I’m sure it makes the linker a tiny bit faster or whatever, but coooooooome on.
Anyway, that’s two hours I’ll never get back, although at least I did learn something…
(see all my programming rants)