_libiconv or _iconv undefined symbol on Mac OSX

2020-02-28 17:46发布

问题:

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?

回答1:

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:

  • find / -name iconv.* (or libiconv.* and you may need to be root to find all of them)
  • If your compile isn't already showing you the -I and -L paths for each thing compiled/linked, then figure out a verbose option so it will (for configure I think just run "export V=1" on the command line and then run configure and make in that same shell window).
  • You would think that specifying "--with-iconv=<path-to-iconv-dir>" as a param for ./configure would take care of this but sometimes it's not guaranteed that the generated Makefile will include you iconv path before another library path that picks up some other iconv files, so you might need to edit the Makefile to really force it to look exactly where you want it to look first.
  • otool -L <executable/dylib> will show you what libraries a binary is linking to.
  • "file filename" will show you what architectures a library has symbols for.
  • Just copy the "cc" command that's failing (one of the lines of output when you run the Makefile) and screw around with adding or ordering the parameters until you figure out a combo that works (i.e., -I, -L, -l, <absolute path to lib file with no preceding -l>) and then work backwards from there to get the Makefile to do the same thing (or just run "make" again to let it continue compiling and linking everything else).
  • Note: Sometimes what happens when you install packages using Homebrew, MacPorts, etc. is that the iconv lib brought in to solve a very specific dependency is now lurking there in your lib and include paths and picked up for other things that rely on the normal MacOSX version -- and all of a sudden you get problems with an unrelated program even though you "didn't change anything."
  • Note: You'll notice there is an iconv lib in /usr/lib but no corresponding header in /usr/include. In fact, there's no /usr/include directory at all. The latest versions of Mac OSX separate out the development headers from the OS. The development headers and libs are handled by Xcode and you can find them by running this: "xcrun --show-sdk-path" and then going to that directory where you will find /include and /lib directories. Explained here: https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes#3035624

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



回答2:

I had the same problem trying to install cargo-tree:

cargo install cargo-tree
<...>
= note: Undefined symbols for architecture x86_64:
        "_iconv", referenced from:
            _git_path_iconv in liblibgit2_sys-966b1b327b8681d4.rlib(path.o)
           (maybe you meant: _git_path_iconv_clear, _git_path_iconv_init_precompose , _git_path_iconv )
        "_iconv_open", referenced from:
            _git_path_direach in liblibgit2_sys-966b1b327b8681d4.rlib(path.o)
<...>

My solution was ot to disable libiconv from MacPorts:

sudo port deactivate libiconv

Then installation succeeded.

You may need to activate libiconv back if some of you Macports applications are not working:

sudo port activate libiconv