How to fix C++ static linking problems?
March 26, 2009 6:33 AM   Subscribe

I need some help diagnosing a problem static linking a C++ program using g++. It compiles and runs just fine without "-static", but I get all sorts of "undefined references" errors when I use "-static". I'm a C++ novice and am at a loss. Can anyone throw me a bone?

I'm using g++ v4.32 running on Debian Lenny.

As I said, it compiles, links, and runs with no problem using dynamic linking. It just breaks with "-static". This is also why I didn't post the source code, though I can if it would be helpful.

Aside from the standard C++ stuff (iostream, ifstream), I'm only using the vmime library and two libraries that it requires (gsasl and gnutls). I installed gsasl and gnutls using apt-get, but I built libvmime from source with no errors. Libvmime's ./configure defaults to building both static and dynamic versions of the library, and I've confirmed that both libvmime.a and libvmime.o exist. I also confirmed that .a files exist for both gsasl and gnutls.

Here's my compile command line:
g++ `pkg-config --cflags --libs vmime` -static -o mimetest mimetest.cpp

The pkg-config bit expands to:
-I/usr/local/include/ -L/usr/local/lib -lvmime -lgnutls -lgsasl

I've confirmed that libvmime exists under /usr/local/lib, and gnutls and gsasl exist under /usr/lib.

I've posted the errors below. Any suggestions?

Thanks,
M
------------------------------------------------------------------------------

/tmp/ccioHkjN.o: In function `loadX509CertificateFromFile(std::basic_string, std::allocator > const&)':
mimetest.cpp:(.text+0x140): undefined reference to `vmime::utility::inputStreamAdapter::inputStreamAdapter(std::basic_istream >&
)'
mimetest.cpp:(.text+0x15d): undefined reference to `vmime::security::cert::X509Certificate::import(vmime::utility::inputStream&)'
/tmp/ccioHkjN.o: In function `main':
mimetest.cpp:(.text+0x2681): undefined reference to `vmime::security::cert::defaultCertificateVerifier::setX509RootCAs(std::vector, std::allocator > > const&)'
mimetest.cpp:(.text+0x2703): undefined reference to `vmime::net::session::getProperties()'
mimetest.cpp:(.text+0x27f5): undefined reference to `vmime::net::session::getProperties()'
mimetest.cpp:(.text+0x28dd): undefined reference to `vmime::net::session::getProperties()'
mimetest.cpp:(.text+0x29c8): undefined reference to `vmime::net::session::getProperties()'
mimetest.cpp:(.text+0x2ab2): undefined reference to `vmime::utility::url::url(std::basic_string, std::allocator > const&)'
mimetest.cpp:(.text+0x2b5c): undefined reference to `vmime::net::session::getStore(vmime::utility::url const&, vmime::utility::ref)'
mimetest.cpp:(.text+0x2c0b): undefined reference to `vmime::net::service::setCertificateVerifier(vmime::utility::ref)'
mimetest.cpp:(.text+0x2d60): undefined reference to `vmime::charsets::UTF_8'
mimetest.cpp:(.text+0x2d72): undefined reference to `vmime::charset::charset(char const*)'

----------------------------------------------------------------
Cut out some...
----------------------------------------------------------------

/tmp/ccioHkjN.o: In function `vmime::header::Date() const':
mimetest.cpp:(.text._ZNK5vmime6header4DateEv[vmime::header::Date() const]+0x1b): undefined reference to `vmime::fields::DATE'
mimetest.cpp:(.text._ZNK5vmime6header4DateEv[vmime::header::Date() const]+0x4a): undefined reference to `vmime::header::findField(std::basic_string, std::allocator > const&) const'
/tmp/ccioHkjN.o: In function `vmime::header::From() const':
mimetest.cpp:(.text._ZNK5vmime6header4FromEv[vmime::header::From() const]+0x1b): undefined reference to `vmime::fields::FROM'
mimetest.cpp:(.text._ZNK5vmime6header4FromEv[vmime::header::From() const]+0x4a): undefined reference to `vmime::header::findField(std::basic_string, std::allocator > const&) const'
/tmp/ccioHkjN.o: In function `vmime::header::Subject() const':
mimetest.cpp:(.text._ZNK5vmime6header7SubjectEv[vmime::header::Subject() const]+0x1b): undefined reference to `vmime::fields::SUBJECT'
mimetest.cpp:(.text._ZNK5vmime6header7SubjectEv[vmime::header::Subject() const]+0x4a): undefined reference to `vmime::header::findField(std::basic_string, std::allocator > const&) const'
/tmp/ccioHkjN.o: In function `vmime::charset::~charset()':
mimetest.cpp:(.text._ZN5vmime7charsetD1Ev[vmime::charset::~charset()]+0xd): undefined reference to `vtable for vmime::charset'
mimetest.cpp:(.text._ZN5vmime7charsetD1Ev[vmime::charset::~charset()]+0x2c): undefined reference to `vmime::component::~component()'
mimetest.cpp:(.text._ZN5vmime7charsetD1Ev[vmime::charset::~charset()]+0x50): undefined reference to `vmime::component::~component()'
/tmp/ccioHkjN.o: In function `vmime::utility::url::~url()':
mimetest.cpp:(.text._ZN5vmime7utility3urlD1Ev[vmime::utility::url::~url()]+0x12): undefined reference to `vmime::propertySet::~propertySet()'
/tmp/ccioHkjN.o:(.rodata._ZTVN5vmime7utility11inputStreamE[vtable for vmime::utility::inputStream]+0x10): undefined reference to `vmime::utility::stream::get
BlockSize() const'
/tmp/ccioHkjN.o:(.rodata._ZTIN5vmime7utility11inputStreamE[typeinfo for vmime::utility::inputStream]+0x8): undefined reference to `typeinfo for vmime::utilit
y::stream'
collect2: ld returned 1 exit status

posted by duoshao to Computers & Internet (9 answers total) 4 users marked this as a favorite
 
This doesn't directly answer your question, but Stack Overflow can often answer these types of questions very quickly.
posted by mkb at 6:58 AM on March 26, 2009


It looks to me like something's screwy with your static library for libvmime.

You can use "nm -g -C libvmime.a" to verify that the symbols that it's looking for are actually in your library.
posted by demiurge at 7:28 AM on March 26, 2009


Best answer: I recently dealt with a linker problem that related to the order of listed components when using static linking.

Basically, the static linker wants to pull in only what it needs from each included library. But it only knows what it needs based on the symbols it has already loaded and looked at. And it only tries to statically link in each library once, when it first sees it.

So when you put mimetest.cpp after the other libraries, those libraries are not really linked in, because the linker doesn't think it needs anything from them yet.

Try putting your source code first on the command line, followed by the library arguments, like this:
g++ -o mimetest mimetest.cpp -static `pkg-config --cflags --libs vmime` 

posted by dkg at 9:19 AM on March 26, 2009


(btw, if yer just learning your way around some section of the language, i strongly recommend using -Wall -Werror --pedantic as flags for g++. It's a good way to get yourself into healthy, maintainable coding habits from the beginning)
posted by dkg at 9:23 AM on March 26, 2009


Seconding dkg. You need to put your source before your external libraries. If you run man ld and look at the description of the -l option, you'll see this:

The linker will search an archive only once, at the location where
it is specified on the command line. If the archive defines a sym-
bol which was undefined in some object which appeared before the
archive on the command line, the linker will include the appropri-
ate file(s) from the archive. However, an undefined symbol in an
object appearing later on the command line will not cause the
linker to search the archive again.
posted by xbonesgt at 9:31 AM on March 26, 2009


one more thought on this (i know it doesn't directly address the question, sorry!): please make sure you really need static linking for a good reason. You're linking in some heavy-duty libraries here with serious security implications. None of these libraries (nor any others, fwict) are bug-free. By statically linking, you are embedding the current version of these libraries, with whatever unknown bugs that they may have right now.

So if there's a security bug found in GNU SASL, and the system administrator upgrades libgsasl7 to resolve the problem, your program will still be buggy. Do you plan on tracking these libraries, and re-issuing new statically-linked binaries whenever a bug is found in any of them? That sounds like a lot of headache.

You're running on debian, so declaring a dependency on the dynamically-linked libraries you need is a better way to go if at all possible: your shipped binaries will be smaller, you can take advantage of system upgrades, you'll consume less RAM (assuming other tools are using the same libs), and your sysadmin will not tear out her hair trying to understand why even though she patched the critical bug, your application is still misbehaving.

I'm not saying there's never a reason to use static linking, just that there are very good reasons to use dynamic linking instead.

Feel free to MeMail me if you want to talk more about this choice.
posted by dkg at 11:19 AM on March 26, 2009


Second dkg's comments. Linkers are very sensitive to what order compiled code is presented to them. The idea is the most dependent code should come first, and so on, and so on down to the most general.

Make sure that it goes: your object file, libvime, anything libvime depends on, etc.
posted by teabag at 1:22 PM on March 26, 2009


Response by poster: First, thanks to everyone. I appreciate the help.

It looks like the suggestions about how the "-l"s are ordered were right on the money -- when I put my cpp file first, the "undefined reference" errors originating from my code all went away.

Unfortunately, they've now been replaced by undefined reference errors originating from gsasl. Sigh... For fun(!), I tried rearranging the order that the libraries get linked -- if I switch the order of gnutls and gsasl I can get it to give me a bunch of errors originating from gnutls.

The points about using shared libraries instead of static are well taken, and good perspective for when I create something "real". For now, though, this project is only a learning exercise that I assigned to myself, so fortunately it won't need to stand-up to security challenges or upgrade cycles.

Also, I didn't know about "nm" before. Seems like that'll be quite useful to me going forward.
posted by duoshao at 4:16 PM on March 26, 2009


If lib A refers to lib B and lib B refers to lib A, you will actually need to list them multiple times:

gcc -lA -lB -lA

I think there is also an option for gcc that allows to cycle over the libraries but I can't find it anymore.

posted by chairface at 5:15 PM on March 26, 2009


« Older "Champagne for my real friends. Real pain for my...   |   1-800 help a wimp Newer »
This thread is closed to new comments.