iconvlibiconv

_libiconv or _iconv undefined symbol on Mac OSX


When compiling some packages from source on Mac OSX, I get the following iconv error:

Undefined symbols for architecture x86_64:
  "_iconv", referenced from:
  "_iconv_close", referenced from:
  "_iconv_open", referenced from:

or I get:

Undefined symbols for architecture x86_64:
"_libiconv", referenced from:
"_libiconv_open", referenced from:
"_libiconv_close", referenced from:

Why does this happen and how can I get around this dependency or, more generally, figure out what is going on and how to fix it?


Solution

  • I have run into this problem over multiple years / upgrades of Mac OSX. I have thoroughly read through all the various answers, of which there are many. This answer is what I wish I had had when I started this journey, so I hope it helps.

    What's happening:

    You have two, possibly three, versions of iconv installed:

    1. One that comes installed with MacOSX in /usr/lib whose function call names are "iconv()", "iconv_open()", "iconv_close", etc.
    2. A GNU libiconv version for *nix systems whose function call names are "libiconv", "libiconv_open()", "libiconv_close()", etc.
    3. Possibly a version installed with Xcode here: /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib

    Whatever program you're compiling/running isn't finding the one it wants.

    This can happen for (at least) 3 reasons:

    1. You have multiple versions of iconv installed on your system and the library search paths are finding another one first. This happens a lot with package managers that bring *nix packages onto MacOSX (like MacPorts, Homebrew, Fink, etc.). They not only bring in the GNU libiconv version but they usually modify the lib search path to hit their dir first (i.e., "_iconv" not found because the first lib it's hitting has the GNU libiconv's "_libiconv" defined instead).
    2. You are completely lacking the iconv version that your code is looking for, usually the GNU libiconv version. I've run across this when compiling PHP from source. It wants the GNU libiconv version (i.e., it calls "libiconv_open()", "libiconv_close()", etc.
    3. You have .h files that the compiler finds from one version but the library search path finds libs from another version first. For instance, if you find the GNU libiconv's iconv.h file first, it redefines "iconv" to "libiconv" but the linker may only be finding the Mac OSX lib in /usr/lib (which has symbols "_iconv", "_iconv_open", "_iconv_close") and you'll get the error can't find "_libiconv" symbols even if you haven't installed the GNU libiconv version of iconv lib anywhere.

    What to do about it:

    Your job is to get whatever you're running/compiling to find the right version of iconv before the others. You can do this in a couple of ways.

    When compiling, you can try including a "--with-iconv=<dir>" and or "--with-iconv-dir=<dir>" or "--with-libiconv-prefix=<dir>" directive when running "configure" to point to the right version. If that doesn't work, you'll need to take a more direct approach like editing the Makefile directly.

    My personal preference is to contain these kinds of changes just to the project having the problem so it doesn't have a cascading impact on unrelated projects later on. For me, that means editing the Makefile created by "configure" and including the iconv lib dir directly in an LDFLAGS entry (or similar) in the Makefile. When you pass "configure" a "--with-iconv=" directive, it's supposed to do that but I have found it doesn't always work because the Makefile will include some other lib dir before the one you want.

    What you are after here is the ordering. You want your iconv lib dir to show up before other lib dirs in the "cc" compile command, so check the output when "make" is run (verbose mode). Also, you could just replace "-liconv" with the absolute path to the lib file itself (i.e., "path/to/iconv/lib/libiconv.dylib" with no -L, no -l).

    In addition, if you see this in a lib path in the Makefile

    /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/lib

    then put it last in that list of library paths and make sure the path to the correct iconv lib dir is before it. Same for this one on the -I include paths:

    /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/include

    Potentially, you could also just go in the unwanted iconv lib dirs and move them to another place (note: this won't work for the MacOSX versions because they're protected by the system).

    How to debug this problem on your system:

    DYLD_LIBRARY_PATH and LD_LIBRARY_PATH

    tl;dr: don't do it.

    In an effort to get your compile to work, you might have added a new path to DYLD_LIBRARY_PATH or LD_LIBRARY_PATH (in ~/.bash_profile or similar) pointing to the GNU iconv libs in order to "help" the linker find things. This will come back to bite you at another time/day when something completely unrelated to what you're doing now is looking for the MacOSX iconv libs and instead pulls in the GNU libiconv libs. So I leave DYLD_LIBRARY_PATH and LD_LIBRARY_PATH alone when it comes to iconv. For other projects that I compile from source, like OpenSSL, yes, I do change DYLD_LIBRARY_PATH and LD_LIBRARY_PATH because so far there haven't been any conflicts on MacOSX.

    Finally, here is a MacPorts support thread discussing this issue that is very illuminating about why this problem exists at all: https://trac.macports.org/ticket/57821