How can I force PHP to use the libiconv version of iconv instead of the CentOS-installed glibc version?

Randell picture Randell · Jan 20, 2011 · Viewed 13.6k times · Source

The code I'm working on runs perfectly on Windows XP and on Mac OS X. When testing it on CentOS (and on Fedora and Ubuntu), it's not working properly. Searching the nets led me to the conclusion that it's the glibc version of the iconv that's causing the problem. So now I need the libiconv version of iconv for Zend Lucene to work properly.

I already downloaded libiconv and configured it with --prefix=/usr/local, make, then make install without any errors. It seems that it was successfully installed because executing /usr/local/bin/iconv --version says the version is the libiconv. Although a simple iconv --version still gives the glibc version.

Then I recompiled PHP from source using --with-iconv=/usr/local. But still, the phpinfo() is showing the iconv being used is the glibc version. I've also already tried several other compiles using --with-iconv-dir or using /usr/local/bin/php.

Of course, I restarted the web server after recompiling PHP.

I have the following line in my /etc/httpd/conf/httpd.conf:

LoadModule /usr/lib/httpd/modules/libphp5.so

and libphp5.so is actually in /usr/lib/httpd/modules.

phpinfo() shows PHP 5.3.3. I also yum removed the pre-installed PHP 5.1.* just to make sure. But the iconv is still using the glibc version.

ldd /usr/lib/httpd/modules/libphp5.so gives

linux-gate.so.1 =>  (0x003b1000)
/usr/local/lib/preloadable_libiconv.so (0x00110000)
libcrypt.so.1 => /lib/libcrypt.so.1 (0x001ed000)
librt.so.1 => /lib/librt.so.1 (0x0021f000)
libmysqlclient.so.15 => /usr/lib/mysql/libmysqlclient.so.15 (0x003b2000)
libldap-2.3.so.0 => /usr/lib/libldap-2.3.so.0 (0x0026e000)
liblber-2.3.so.0 => /usr/lib/liblber-2.3.so.0 (0x00370000)
libiconv.so.2 => /usr/local/lib/libiconv.so.2 (0x00516000)
libfreetype.so.6 => /usr/lib/libfreetype.so.6 (0x002a8000)
libpng12.so.0 => /usr/lib/libpng12.so.0 (0x00228000)
libz.so.1 => /usr/lib/libz.so.1 (0x00328000)
libcurl.so.3 => /usr/lib/libcurl.so.3 (0x00f23000)
libm.so.6 => /lib/libm.so.6 (0x0033b000)
libdl.so.2 => /lib/libdl.so.2 (0x00364000)
libnsl.so.1 => /lib/libnsl.so.1 (0x0037e000)
libxml2.so.2 => /usr/lib/libxml2.so.2 (0x00f5f000)
libssl.so.6 => /lib/libssl.so.6 (0x0862c000)
libcrypto.so.6 => /lib/libcrypto.so.6 (0x04145000)
libgssapi_krb5.so.2 => /usr/lib/libgssapi_krb5.so.2 (0x08e2d000)
libkrb5.so.3 => /usr/lib/libkrb5.so.3 (0x0611a000)
libk5crypto.so.3 => /usr/lib/libk5crypto.so.3 (0x005f4000)
libcom_err.so.2 => /lib/libcom_err.so.2 (0x0024e000)
libidn.so.11 => /usr/lib/libidn.so.11 (0x071f5000)
libc.so.6 => /lib/libc.so.6 (0x08aa6000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00397000)
/lib/ld-linux.so.2 (0x00251000)
libresolv.so.2 => /lib/libresolv.so.2 (0x0748a000)
libsasl2.so.2 => /usr/lib/libsasl2.so.2 (0x07ddf000)
libkrb5support.so.0 => /usr/lib/libkrb5support.so.0 (0x062b7000)
libkeyutils.so.1 => /lib/libkeyutils.so.1 (0x00369000)
libselinux.so.1 => /lib/libselinux.so.1 (0x0913b000)
libsepol.so.1 => /lib/libsepol.so.1 (0x07eb4000)

This is a cross-post from: NullPointer.ph

Answer

peoro picture peoro · Jan 30, 2011

Your module (libphp5.so) is linked to two shared libraries which are providing the same symbol (in this case the symbol is iconv and the libraries are libiconv.so.2 and probably libc.so.6).

When this happen, the first loaded symbol is used: probably libc.so.6 gets loaded before libiconv.so.2 and thus it's the one providing you with the iconv symbol.

You can force the dynamic loader to load a library before any other; you can do this by setting the LD_PRELOAD environment variable to the library you want to preload.

I'm not an expert about Apache, so I'm not totally sure about how it works, how it starts its process and what processes it uses, but I think that setting LD_PRELOAD before running apache should do the trick:

LD_PRELOAD=/usr/local/lib/libiconv.so.2

A little example to show LD_PRELOAD in action:

Will compile myfopen.c as a shared library (myfopen.so): it will provide a fopen symbol (already defined in libc):

$ cat myfopen.c
int fopen(const char *path, const char *mode){ return -1; }
$ gcc -o libmyfopen.so myfopen.c -shared

Compiling printfopen.c as an executable (printfopen) which just prints the result of fopen; will link it against both libc and libmyfopen (LD_LIBRARY_PATH is needed to let the linker look for the libraries also in .):

$ cat printfopen.c
#include <stdio.h>
int main( ) {
    printf( "%d\n", fopen("","") );
    return 0;
}
$ gcc -o printfopen printfopen.c -L. -lmyfopen
$ LD_LIBRARY_PATH=. ldd printfopen
    linux-gate.so.1 =>  (0xb779d000)
    libmyfopen.so => ./libmyfopen.so (0xb779a000)
    libc.so.6 => /lib/libc.so.6 (0xb762f000)
    /lib/ld-linux.so.2 (0xb779e000)

Now I'm running it, to test if LD_PRELOAD works:

$ LD_LIBRARY_PATH=. ./printfopen
-1
$ LD_PRELOAD=/lib/libc.so.6 LD_LIBRARY_PATH=. ./printfopen
0
$ LD_PRELOAD=libmyfopen.so LD_LIBRARY_PATH=. ./printfopen
-1

By default it loads libmyfopen before libc, then I tried to force loading libc and then libmyfopen first.

I guess that in your case libc is getting loaded before libiconv because the former is loaded by the application (apache?) before it loads the PHP module.