Our Blog

Ongoing observations by End Point people

Debugging PHP extensions with the dynamic linker

By Jason Dixon · February 25, 2011

Tags: linux, php, tools

If you've ever had to track down a missing dependency or incompatible libraries, chances are good that you were assisted by the ldd command. This helpful utility reports the list of shared library dependencies required by a binary executable. Or in the typical use case, it will tell you which libraries are missing that your application needs to run.

Fortunately most Linux and BSD distributions do a decent job of enforcing dependencies with their respective package managers. But inevitably, there's the occasional proprietary, closed-binary third-party application or built-from-source utility that skirts the convenience of mainstream distributions. If you're lucky, the vendor accurately details which software is required, including specific versions theirs was built against. If you're not, well, you might have to resort to tools like ldd, or even process tracers like strace (Linux), ktrace (OpenBSD) and truss (Solaris).

I recently had the misfortune of troubleshooting a PHP application that was unable to load imagick.so, a native PHP extension to create and modify images using the ImageMagick API. The problem manifested itself innocently enough:

PHP Warning:  PHP Startup: Unable to load dynamic library '/var/www/lib/php/modules
  /imagick.so' - Cannot load specified object in Unknown on line 0

Naturally, my first step was to verify the extension was installed.

$ ls -l /var/www/lib/php/modules/
total 7336
-r--r--r--  1 root  bin       70586 Aug 10  2010 curl.so
-r--r--r--  1 root  bin      395883 Aug 10  2010 gd.so
-rwxr-xr-x  1 root  bin      489386 Aug 10  2010 imagick.so
-r--r--r--  1 root  bin     2105515 Aug 10  2010 mbstring.so
-r--r--r--  1 root  bin       45476 Aug 10  2010 mcrypt.so
-r--r--r--  1 root  bin       63643 Aug 10  2010 mysql.so

Knowing that I'd installed the pecl-imagick package using OpenBSD's pkg_add (which handles dependencies nicely), it seemed unlikely that it was missing any mainstream dependencies. Logically, this started me thinking that it might be a problem with OpenBSD's default chroot for Apache. Needless to say I was disappointed to see the same error when I ran php -m from the command line:

$ php -m 2>&1 | grep imagick
PHP Warning:  PHP Startup: Unable to load dynamic library
'/var/www/lib/php/modules/imagick.so' - Cannot load specified object in
Unknown on line 0

That was unexpected, but not altogether surprising. Let's take a quick look with ldd to see what the extension thinks is missing:

$ ldd /var/www/lib/php/modules/imagick.so
Cannot load specified object

That's disturbing... and not much help at all. By contrast, the curl.so extension gives back a full list of shared objects:

$ ldd /var/www/lib/php/modules/curl.so                                                                              
        Start            End              Type Open Ref GrpRef Name
        00000002065a3000 00000002069b2000 dlib 1    0   0      /var/www/lib/php/modules/curl.so
        000000020ac30000 000000020b07e000 rlib 0    1   0      /usr/local/lib/libcurl.so.15.0
        000000020f3d4000 000000020f807000 rlib 0    2   0      /usr/local/lib/libidn.so.16.30
        0000000204b21000 0000000204f71000 rlib 0    2   0      /usr/lib/libssl.so.15.1
        0000000204150000 00000002046de000 rlib 0    2   0      /usr/lib/libcrypto.so.18.0
        00000002087f0000 0000000208c05000 rlib 0    2   0      /usr/lib/libz.so.4.1
        0000000209100000 000000020950a000 rlib 0    2   0      /usr/local/lib/libintl.so.5.0
        0000000208c05000 0000000209100000 rlib 0    2   0      /usr/local/lib/libiconv.so.6.0

At this point I determine there has to be something wrong with imagick.so; the wild goose chase begins. I reinstall the pecl-imagick package from a variety of sources, thinking the original mirror might have a corrupted package. Next I rebuild the package manually from OpenBSD's ports tree. No change.

Finally, one of the OpenBSD developers suggested the LD_DEBUG environment variable. This tells the run-time link-editor (ld.so) to increase verbosity. The advantage this has over ldd is that it will catch any attempt to load shared objects after startup. In the case of PHP, it will look at any shared objects when php tries to load dynamic extensions with dlopen().

$ sudo LD_DEBUG=1 php -m 2>&1 | more
dlopen: loading: /var/www/lib/php/modules/imagick.so
head /var/www/lib/php/modules/imagick.so
obj /var/www/lib/php/modules/imagick.so has /var/www/lib/php/modules/imagick.so as head
linking /var/www/lib/php/modules/imagick.so as dlopen()ed
head [/var/www/lib/php/modules/imagick.so]
examining: '/var/www/lib/php/modules/imagick.so'
loading: libjbig.so.2.0 required by /var/www/lib/php/modules/imagick.so
obj /usr/local/lib/libjbig.so.2.0 has /var/www/lib/php/modules/imagick.so as head
loading: libm.so.5.2 required by /var/www/lib/php/modules/imagick.so
loading: libX11.so.13.0 required by /var/www/lib/php/modules/imagick.so
-->> dlopen: failed to open libX11.so.13.0 <<--
unload_shlib called on /var/www/lib/php/modules/imagick.so
unload_shlib unloading on /var/www/lib/php/modules/imagick.so
dlopen: /var/www/lib/php/modules/imagick.so: done (failed).
PHP Warning:  PHP Startup: Unable to load dynamic library '/var/www/lib/php/modules/imagick.so' - 
   Cannot load specified object in Unknown on line 0

And there's our missing library (libX11.so.13.o). In this case, the dependency was installed on the system, but it's path (/usr/X11R6/lib) wasn't in the shared library cache. I had remembered to install all of the X11 libraries needed for the ImageMagick and GD libraries, but had forgotten to update the cache with ldconfig. A few seconds and a couple commands later, we were back in business.

$ sudo ldconfig -m /usr/X11R6/lib
$ php -m | grep imagick