Friday, October 26, 2012

Dealing with exit code 5 when running Ghostscript under MAMP


I had been using Ghostscript for a project, calling it from a PHP script running in a MAMP environment on Snow Leopard. Several months later, on a different computer, running a newer version of MAMP (2.1.1), on Lion, I kept getting some weird errors.

When GS was called through passthru, like so:

passthru('gs -version', $retval);

I wouldn't get the expected output, indicating the version of GS, instead the command would be completely silent and  $retval would be set to 5, indicating an error (on success it should have been 0).

Running the same command in a regular terminal window would produce the expected result:

$ gs -v
GPL Ghostscript 9.06 (2012-08-08)
Copyright (C) 2012 Artifex Software, Inc.  All rights reserved.

My intuition told me that it may be a problem of the environment used by PHP interpreter running under MAMP. Taking a look it shows this:


Next step was to reproduce the same environment on the terminal, and surely the result confirmed my guess:

$ ./gs -version            
dyld: Symbol not found: _iconv
  Referenced from: /usr/lib/libcups.2.dylib
  Expected in: /Applications/MAMP/Library/lib/libiconv.2.dylib
 in /usr/lib/libcups.2.dylib
Trace/BPT trap: 5

Later I discovered that the same error also shows up in the Apache error log under MAMP, albeit without explicitly showing the error 5.


The problem seems to be caused by the dylibs under /Applications/MAMP/Library/lib/. MAMP seems to be prompted to look there by the DYLD_LIBRARY_PATH environment variable that comes with the default config (set by /Applications/MAMP/Library/bin/envvars).

It seems that the dylibs used by MAMP are different than those used by a default terminal session. A regular shell session, ran from terminal, loads dylibs from /usr/lib. If I do this:


$ nm /usr/lib/libiconv.2.dylib | grep _iconv
00000000000f1af0 S ___iconv_2VersionNumber
00000000000f1b90 S ___iconv_2VersionString
000000000000a1e1 T _iconv
000000000000a5a0 T _iconv_canonicalize
0000000000013164 T _iconv_close
0000000000013171 T _iconv_open
000000000000a72c T _iconvctl
000000000000a20f T _iconvlist


it shows the _iconv symbol present in that version of the lib.

However, if I look in the version used by MAMP, it's missing:

$ nm /Applications/MAMP/Library/lib/libiconv.2.dylib | grep _iconv
0000000000029c50 T _iconv_canonicalize

Instead those symbols seem to have an extra "lib" prefix:

$ nm /Applications/MAMP/Library/lib/libiconv.2.dylib | grep iconv
000000000010d748 D __libiconv_version
0000000000029c50 T _iconv_canonicalize
0000000000028a90 T _libiconv
0000000000028b40 T _libiconv_close
00000000000280c0 T _libiconv_open
0000000000028b80 T _libiconv_open_into
000000000002a7c0 t _libiconv_relocate
000000000002a310 T _libiconv_set_relocation_prefix
0000000000029500 T _libiconvctl
0000000000029870 T _libiconvlist

It seems that MAMP loads libcups.2.dylib from /usr/lib/ and its libiconv.2.dylib dependency from /Applications/MAMP/Library/lib. That's, I believe, the root of the problem. Those 2 libraries have incompatible versions. That's confirmed by:

$ otool -L /usr/lib/libcups.2.dylib/usr/lib/libcups.2.dylib:
/usr/lib/libcups.2.dylib (compatibility version 2.0.0, current version 2.9.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos (compatibility version 5.0.0, current version 6.0.0)
/System/Library/Frameworks/GSS.framework/Versions/A/GSS (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 635.21.0)
/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration (compatibility version 1.0.0, current version 395.11.0)
/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 46.1.0)
/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 55148.1.0)
/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)

and:

$ otool -L /Applications/MAMP/Library/lib/libiconv.2.dylib
/Applications/MAMP/Library/lib/libiconv.2.dylib:
/Applications/MAMP/Library/lib/libiconv.2.dylib (compatibility version 8.0.0, current version 8.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.11)

A workaround may be as easy as easy as re-building libcups.2.dylib from sources, and configure it to pick the libs from /Applications/MAMP/Library/lib/.


$ cd cups-1.6.1
$ ./configure CC=clang CXX=clang++ LDFLAGS=-L/Applications/MAMP/Library/lib 
$ make
$ cp ./cups/libcups.2.dylib /Applications/MAMP/Library/lib/

Seems to fix the problem with my script, so I can move on with my work. The question is how sane this rebuilt libcups.2.dylib is.

Doing

$ otool -L ./cups/libcups.2.dylib

./cups/libcups.2.dylib:
/usr/lib/libcups.2.dylib (compatibility version 2.0.0, current version 2.10.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos (compatibility version 5.0.0, current version 6.0.0)
/System/Library/Frameworks/GSS.framework/Versions/A/GSS (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 635.21.0)
/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration (compatibility version 1.0.0, current version 395.11.0)
/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 46.1.0)
/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 55148.1.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)


shows the dependency on /usr/lib/libiconv.2.dylib gone. A bit surprising.


A quick sanity check done like this:
$ make check 


has most of the tests pass, except internationalization API tests, which is not that surprising, since I suppose libiconv.2.dylib would play an important role there.

All in all, that's how I was able to get around this problem with MAMP. I posted this issue on their forum, but almost 24 hr later it hasn't even cleared the moderator, let alone gotten a better solution.




2 comments:

Dr Street said...

Thanks, I am having a similar problem with running R from MAMP 2.0.2. I'd love to try your solution but I don't know how to go about "re-building libcups.2.dylib from sources" - can you tell me where I can download the source files? thanks!

Unknown said...

@Dr Street: I don't remember exactly, but my Downloads folder shows a cups-1.6.1-source.tar.bz2 file, which if googled points to www.cups.org/software.php, which appears to be a link I visited in the past. Most likely that's where I took it from. Good luck !