How can I force PHP to use the libiconv version of

2019-03-13 09:42发布

问题:

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

回答1:

I just have changed my php-5.3.3 from glibc's iconv to GNU libiconv through the manual recompiling of the php iconv extension. Follow these steps:

  1. download php-5.3.3 source code package
  2. extract it and go into php-5.3.3/ext/iconv subdirectory
  3. execute phpize command (if you have no such command then install php-devel package)
  4. (*) edit configure file (vim configure): add iconv_impl_name="" at 4664 line (exact line number on your system configuration may be different):

    ...
    iconv_impl_name=""
        if test -z "$iconv_impl_name"; then
          { $as_echo "$as_me:${as_lineno-$LINENO}: checking if using GNU libiconv" >&5<
    ...
    
  5. ./configure --with-iconv=/usr/local|grep iconv:

    checking if using GNU libiconv... yes
    
  6. make

  7. sudo make install

And now I run php -i|grep "iconv impl" and got:

iconv implementation => libiconv

* This trick forces configure to select the GNU libiconv instead of glibc's iconv. By default it checks for glibc's iconv at first step and does not check for GNU libiconv at all.



回答2:

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.



回答3:

Are you sure that LD_LIBRARY_PATH is set properly for httpd (web server) process? If not, try setting it like:

export LD_LIBRARY_PATH="/usr/local/lib:${LD_LIBRARY_PATH}"

... in the script that starts the process (i.e. apachectl).

The ldd output you showed looks correct, but you invoked ldd from user's environment, and httpd's might be different.

It could also help to set PATH to "/usr/local/bin:${PATH}", just in case.



回答4:

I understand, that this question is already answered and almost dead, but recently I tried to find a way to compile PHP with libiconv because in PHP I couldn't convert "∙" from UTF8 to CP1251 even with iconv //IGNORE. But I found another solution that worked for me without recompilation (just use //TRANSLIT):

iconv("UTF8", "CP1251//TRANSLIT//IGNORE", $text)

//TRANSLIT will transliterate ONLY unknown characters (not all, as some may guess), so it converts Russian 'ё', but transliterates unknown '∙' to 0x95 (which looks the same in the target charset).



回答5:

I do not know about CentOS, but in debian based distributions, like Ubuntu, you can choose the version of the program that you want to lauch defining the symbolic links at /etc/alternatives.

So, if you change the symbolink link /etc/alternatives/iconv in order to point to /usr/local/bin/iconv, this point forward it should use the correct version.

http://www.debian-administration.org/articles/91