Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trouble installing PHP 5.3.0 with intl-support

Currently I'm trying to install PHP 5.3.0 on some Linux testing server. As we've urgently waited for ext/intl we want to check out the features it provides. I'm running configure successfully with the following arguments

./configure
    --with-apxs2=/usr/local/apache2/bin/apxs
    --prefix=/usr/local/php
    --with-zlib-dir=/usr/local/zlib
    --with-imap=/.../imap-2006k
    --with-imap-ssl
    --with-openssl=shared
    --with-iconv=shared
    --with-zlib=shared
    --with-curl=shared
    --with-curlwrappers
    --enable-exif
    --with-ldap=shared,/usr/local/openldap
    --with-ldap-sasl
    --enable-mbstring=shared
    --with-mcrypt
    --enable-soap=shared
    --enable-sockets
    --enable-zip=shared
    --enable-pdo=shared
    --with-pdo-sqlite=shared
    --with-sqlite=shared
    --with-mysql=shared,/usr/local/mysql
    --with-pdo-mysql=shared,/usr/local/mysql
    --with-mysqli=shared,/usr/local/mysql/bin/mysql_config
    --with-mhash=shared,/usr/local/mhash
    --with-libxml-dir=/usr/local/libxml2
    --with-xsl=shared,/usr/local/libxslt
    --enable-xmlreader=shared
    --enable-xmlwriter=shared
    --with-gmp=shared
    --with-icu-dir=/usr/local/icu
    --enable-intl

ICU 4.2 is located at /usr/local/icu and PHP 5.2.9 compiled flawlessly (without the int- and icu-options). But when I complie the PHP 5.3.0 source I get a whole lot of error messages of the kind

ext/intl/grapheme/.libs/grapheme_util.o(.text+0xbab):/.../php-5.3.0/ext/intl/grapheme/grapheme_util.c:208: undefined reference to `ubrk_close_4_2'

I'm quite sure it has something to do with not finding the shared libraries. Setting

export LD_LIBRARY_PATH=/usr/local/icu/lib

doesn't help.

Can anyone point me to some solution? I'm rather clueless - and I'm no real expert in these things...

EDIT:

I just rechecked and made sure that the various icu-libraries and the respective soft links are all located in /usr/local/icu/lib:

lrwxrwxrwx  1 root root       20 Jul  1 09:56 libicudata.so -> libicudata.so.42.0.1
lrwxrwxrwx  1 root root       20 Jul  1 09:56 libicudata.so.42 -> libicudata.so.42.0.1
-rw-r--r--  1 root root 16015140 Jul  1 09:56 libicudata.so.42.0.1
lrwxrwxrwx  1 root root       20 Jul  1 09:56 libicui18n.so -> libicui18n.so.42.0.1
lrwxrwxrwx  1 root root       20 Jul  1 09:56 libicui18n.so.42 -> libicui18n.so.42.0.1
-rwxr-xr-x  1 root root  2454770 Jul  1 09:56 libicui18n.so.42.0.1
lrwxrwxrwx  1 root root       18 Jul  1 09:56 libicuio.so -> libicuio.so.42.0.1
lrwxrwxrwx  1 root root       18 Jul  1 09:56 libicuio.so.42 -> libicuio.so.42.0.1
-rwxr-xr-x  1 root root    65299 Jul  1 09:56 libicuio.so.42.0.1
lrwxrwxrwx  1 root root       18 Jul  1 09:56 libicule.so -> libicule.so.42.0.1
lrwxrwxrwx  1 root root       18 Jul  1 09:56 libicule.so.42 -> libicule.so.42.0.1
-rwxr-xr-x  1 root root   356125 Jul  1 09:56 libicule.so.42.0.1
lrwxrwxrwx  1 root root       18 Jul  1 09:56 libiculx.so -> libiculx.so.42.0.1
lrwxrwxrwx  1 root root       18 Jul  1 09:56 libiculx.so.42 -> libiculx.so.42.0.1
-rwxr-xr-x  1 root root    75110 Jul  1 09:56 libiculx.so.42.0.1
lrwxrwxrwx  1 root root       18 Jul  1 09:56 libicutu.so -> libicutu.so.42.0.1
lrwxrwxrwx  1 root root       18 Jul  1 09:56 libicutu.so.42 -> libicutu.so.42.0.1
-rwxr-xr-x  1 root root   159330 Jul  1 09:56 libicutu.so.42.0.1
lrwxrwxrwx  1 root root       18 Jul  1 09:56 libicuuc.so -> libicuuc.so.42.0.1
lrwxrwxrwx  1 root root       18 Jul  1 09:56 libicuuc.so.42 -> libicuuc.so.42.0.1
-rwxr-xr-x  1 root root  1660769 Jul  1 09:56 libicuuc.so.42.0.1

make check runs tons of tests - all of them successfully:

[All tests passed successfully...]
Elapsed Time: 00:00:25.000
make[2]: Leaving directory `/.../icu-4.2/source/test/cintltst'
---------------
ALL TESTS SUMMARY:
All tests OK:  testdata intltest iotest cintltst
make[1]: Leaving directory `/.../icu-4.2/source/test'
make[1]: Entering directory `/.../icu-4.2/source'
verifying that icu-config --selfcheck can operate
verifying that make -f Makefile.inc selfcheck can operate
PASS: config selfcheck OK
make[1]: Leaving directory `/.../icu-4.2/source'

EDIT: answers to VolkerK's questions

I installed ICU 4.2 from source and as I wrote above the build process, the unit-tests and the installation all went fine.

/usr/local/icu/bin/icu-config --version
4.2.0.1

/usr/local/icu/bin/icu-config --prefix
/usr/local/icu

/usr/local/icu/bin/icu-config --cppflags-searchpath
-I/usr/local/icu/include

/usr/local/icu/bin/icu-config --ldflags --ldflags-icuio
-lpthread -lm   -L/usr/local/icu/lib -licui18n -licuuc -licudata  -lpthread -lm   -licuio

objdump -C /usr/local/icu/lib/libicuuc.so.42.0.1
// doesn't work because of unrecognized argument -C

EDIT regarding VolkerK's comment:

No, there has been no switch of the compiler involved - I ran both build processes directly one after the other. objdump /usr/local/icu/lib/libicuuc.so.42.0.1 doesn't work either but I managed to run

objdump -t /usr/local/icu/lib/libicuuc.so.42.0.1 | grep ubrk_close
00000000000d2484 g     F .text  000000000000002d              ubrk_close_4_2

Don't know if this information can help.

EDIT on VolkerK's edit1 and edit2:

I think there's the rub - there is indeed another icu-version on the sytem; at least in parts (there is no other icu-config for example; only the one in /usr/local/icu/bin).

gcc -lpthread -lm -L/usr/local/icu/lib -licui18n -licuuc -licudata -lpthread -lm -licuio -print-file-name=libicuuc.so returns

/usr/lib64/gcc-lib/x86_64-suse-linux/3.3.5/../../../../lib64/libicuuc.so

while gcc -lpthread -lm -L/usr/local/icu/lib -licui18n -licuuc -licudata -lpthread -lm -licuio -print-file-name=libicuuc.so.42 returns

libicuuc.so.42

So the problem seems to be, how to get the new lib-path into the build process?? By the way, I learned a lot from your answers - thank's to all of you.

I also tried to compile your simple test program - and it also fails with the same undefined reference error, most likely due to the same reason PHP won't compile.

How can I get rid of the reference to the old icu-library in the lib-path or how do I prioritize the new icu-library-path?

like image 226
Stefan Gehrig Avatar asked Jul 03 '09 08:07

Stefan Gehrig


1 Answers

The problem seems to be that the binary is linked against the wrong (shared) library files.
First a long, boring explaination of what I think the problem is. Keep in mind that I'm not a linux expert. I really want you to understand my train of thoughts so that you can decide if it's feasible and/or where I'm wrong.
The first (crude) solution is easily reversible. Run another ./configure and all changes are history. I think it's pretty save.

Why do you have icu 4-2 specific dependencies in the first place? Let's take a look at a source file of php's intl extension (ext/intl/grapheme/grapheme_string.c)

#include <unicode/ubrk.h>
...
PHP_FUNCTION(grapheme_substr)
{
   ...
   ubrk_close(bi);
   ...

Until now there's no version specific code. grapheme_string.c looks the same whether you use icu 3.4 or icu 4.2. Where does the ubrk_close_4_2 come from?
When you run the "./configure ... --with-icu-dir=/usr/local/icu" command the file ext/intl/config.m4 is executed. In this process icu-config is called to get the include path and library files required to build php. You provided a path to your icu installation which boils down to that

ICU_CONFIG="$PHP_ICU_DIR/bin/icu-config"
ICU_INCS=`$ICU_CONFIG --cppflags-searchpath`
ICU_LIBS=`$ICU_CONFIG --ldflags --ldflags-icuio`

is executed. You've tried icu-config yourself, so you know what it outputs and therefore what ICU_INCS and ICU_LIBS contain. ICU_INCS and ICU_LIBS are passed to gcc when the files are compiled/linked. gcc (apperently) didn't find unicode/ubrk.h in it's default directory, so it searched for the file in the additional include directories provided by ICU_INCS where it found the icu 4.2 include files. unicode/ubrk.h includes unicode/utypes.h which then includes unicode/urename.h - and again the icu 4.2 header files are included. In this case unicode/urename.h includes #define ubrk_close ubrk_close_4_2.
When the preprocessor is done ubrk_close(bi) has been replaced by ubrk_close_4_2(bi).

PHP_FUNCTION(grapheme_substr)
{
   ...
   ubrk_close_4_2(bi);
   ...

Now you have a version specific dependency, a reference to ubrk_close_4_2 that some library has to resolve.
So the include part did work. It did indeed find your icu 4.2 version and used its header files. So far so good.
Now for the linker part. In your case ICU_LIBS contains

-lpthread -lm -L/usr/local/icu/lib -licui18n -licuuc -licudata -lpthread -lm -licuio

-licuuc tells gcc "find me a library called 'icuuc' and use it". gcc then searches the LIB paths for files with a certain naming scheme that match "icuuc".
In this case libicuuc.so. Note that it doesn't look for a version specific file name, just libicuuc.so. Once it has found such a file it won't look for another one. First gcc searches in its default paths. Then it searches the additional library paths - in the order they are provided to gcc. I.e.

gcc -L/usr/lib -L/usr/local/lib -licuuc

will find /usr/lib/libicuuc.so if there is such a file and not /usr/local/lib/libicuuc.so (anymore). Meaning that either the default path or the order of the library path directives may be the cause of your trouble.
When your program is linked against shared objects a "special" loader is added to the code and the name of the shared object is stored in your program (at link time).
Every time your program is executed, first the (runtime) loader searches for the shared object (by its name), loads the code and replaces some stub jump addresses.
The shared object can "tell" the linker (i.e. at link time) the name of the shared object the loader should look for (SONAME property) at runtime. Take a look at the directory listing you provided in your question text

lrwxrwxrwx  1 root root       18 Jul  1 09:56 libicuuc.so -> libicuuc.so.42.0.1
lrwxrwxrwx  1 root root       18 Jul  1 09:56 libicuuc.so.42 -> libicuuc.so.42.0.1
-rwxr-xr-x  1 root root  1660769 Jul  1 09:56 libicuuc.so.42.0.1

libicuuc.so, that's the file gcc is looking for when -licuuc is provided. The linker follows the symlink and uses libicuuc.so.42.0.1. This file "tells" the linker that the (runtime) loader should look for libicuuc.so.42, see http://userguide.icu-project.org/packaging#TOC-ICU-Versions.
The loader will follow the symlink and load libicuuc.so.42.0.1, or if there is another bugfix libicuuc.so.42.0.2, libicuuc.so.42.0.3, whatever libicuuc.so.42 is pointing to. libicuuc.so.42 will/should always point to an actual shared object that exports the icu 4.2 symbols. The code may have changed/fixed but the exported symbols stay the same. Your problem now is that gcc doesn't find libicuuc.so->libicuuc.so.42.0.1 but (let's say) libicuuc.so->libicuuc.so.34.x.y. This libicuuc.so.34.x.y doesn't export the icu 4.2 symbols, it doesn't provide ubrk_close_4_2 but ubrk_close_3_4. So, no ubrk_close_4_2 -> unresolved reference error.

First "solution" (crude): Let ./configure do its magic and then ... just edit the Makefile.
Open the Makefile (in the source top directory) in a text editor, search for INTL_SHARED_LIBADD= and replace

-licui18n -licuuc -licudata -licuio

in that line by

/usr/local/icu/lib/libicui18n.so.42 /usr/local/icu/lib/libicuuc.so.42 /usr/local/icu/lib/libicudata.so.42 /usr/local/icu/lib/libicuio.so.42

(leave any -lm -pthread ... as they are). Compile again.
This "tells" the gcc/linker not to search for the .so files but to use the specific ones. The result should be the same as if your library path was working (beacuse of SONAME).
But every time you run ./configure you have to apply the "fix" again.

Second solution: Remove the other libicuXY.so symlinks (that's where the word "backup" comes to mind), only keep the libicuXY.so->libicuXY.so.42.0.1 links. If there are no other libicuuc.so->>libicuuc.so.34.x.y links the gcc/linker can't find them and won't link against the old versions.
Again because of the SONAME property binaries that have already been linked against the old version will still function because "their" loader will search for the (still existing) libicuXY.so.34 files.
This will affect all subsequent linker runs, i.e. if you build another project that uses the older include files you will run into the same problem the other way around. The header files and the shared objects (at link time) must match.

like image 199
VolkerK Avatar answered Oct 17 '22 23:10

VolkerK