在mac上开启php的embed模式

看到Laruence大牛的一篇用embed方式调用zend,实现了一个opcode dumper的文章,感觉很有意思,便试着在mac上也搞一个出来玩儿玩儿。没想到一弄就是两天,诸多挫折,一一记录下来,方便后来人~

最难的是编译,之前从未在mac上搞过动态库,以为所有的类unix系统都差不多,动态库就是.so结尾的share object。套用一个老人的话说,你们啊,too…sometimes…。在此我觉得还是有必要说下mac系统下的动态库。

.dylib(shared libraries)和.so(loadable modules)结尾的库,这两个东西在ELF系统上(linux)是一个东西,而在Mach-O(mac)上,它们还是有些区别的。用otool -hv xxx.dylib可以看到file type是DYLIB,再看看***.so,filetype为BUNDLE。fink这里有一篇文章说道:以.dylib结尾的库,是被用来链接的,即可以在链接时使用-lxxx,链接一个名为libxxx.dylib的库(也不知道这么说对不对)。而以.so结尾的库是可以用dyld的API进行加载的,在编写php模块的时候,我们可以使用dlopen来打开一个编译好的.so库,调用里面的函数。

PHP源码里的configure –help中提到–enable-embed有两个类型:shared和static,默认为shared,如果你开启了–enable-embed,configure之后可以看看你Makefile里的OVERALL_TARGET是什么,我这里是libphp5.so。之前没有如前所述那么系统的概念,所以我毫不犹豫的编译了。兴奋地将最后得到的libs/libphp5.so弄到/usr/lib,执行opdump(想知道怎么得到这样一个dumper的同学,请移步使用PHP Embed SAPI实现Opcodes查看器),系统会报出一个非常令人沮丧的错误:
[cce code=”bash”]
can’t link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)
[/cce]
我第一次看到它的时候,是一头雾水啊,直接放到google上搜索,啥都搜索不到啊,有木有?只好从原理上分析了,于是就有了上面那段文字。解决的办法相信你也很清楚了,我们需要的是一个可以被用来链接的库,编译一个libphp5.dylib出来就好啦!这要怎么编译呢?其实只要Makefile里添加一个libphp5.dylib的目标就可以,用LIBTOOL,指定链接为-dynamiclib就成了,如果你想用比完美的方法达到目的,可以把以下这个补丁放到php的源码目录,然后patch -p1 -b < patch就成了。整理自marc:php-interal的邮件列表。可以看到这个日期。。。到现在了这个问题居然还没有解决。。。而牛人的解决方案在php-5.3.5时代依然奏效。。。

patch:
[cce code=”bash”]

Index: sapi/embed/config.m4
===================================================================
--- sapi/embed/config.m4	(revision 314182)
+++ sapi/embed/config.m4	(working copy)
@@ -18,6 +18,10 @@
       PHP_EMBED_TYPE=static
       INSTALL_IT="\$(mkinstalldirs) \$(INSTALL_ROOT)\$(prefix)/lib; \$(INSTALL) -m 0644 $SAPI_STATIC \$(INSTALL_ROOT)\$(prefix)/lib"
       ;;
+    dylib)
+      PHP_EMBED_TYPE=dylib
+      INSTALL_IT="\$(mkinstalldirs) \$(INSTALL_ROOT)\$(prefix)/lib; \$(INSTALL) -m 0755 $SAPI_DYLIB \$(INSTALL_ROOT)\$(prefix)/lib"
+      ;;
     *)
       PHP_EMBED_TYPE=no
       ;;
Index: Makefile.global
===================================================================
--- Makefile.global	(revision 314182)
+++ Makefile.global	(working copy)
@@ -19,6 +19,10 @@
 	$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -rpath $(phptempdir) $(EXTRA_LDFLAGS) $(LDFLAGS) $(PHP_RPATHS) $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) -o $@
 	-@$(LIBTOOL) --silent --mode=install cp $@ $(phptempdir)/$@ >/dev/null 2>&1

+libs/libphp$(PHP_MAJOR_VERSION).dylib: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS)
+	$(LIBTOOL) --mode=link $(CC) -dynamiclib -install_name $(INSTALL_ROOT)$(prefix)/lib/$@ -current_version $(PHP_VERSION) -compatibility_version $(PHP_MAJOR_VERSION) -undefined dynamic_lookup $(PHP_RPATHS) $(PHP_GLOBAL_OBJS:.lo=.o) $(PHP_SAPI_OBJS:.lo=.o) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) -o $@
+	-@$(LIBTOOL) --silent --mode=install cp $@ $(phptempdir)/$@ >/dev/null 2>&1
+
 libs/libphp$(PHP_MAJOR_VERSION).bundle: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS)
 	$(CC) $(MH_BUNDLE_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) $(EXTRA_LDFLAGS) $(PHP_GLOBAL_OBJS:.lo=.o) $(PHP_SAPI_OBJS:.lo=.o) $(PHP_FRAMEWORKS) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) -o $@ && cp $@ libs/libphp$(PHP_MAJOR_VERSION).so

Index: configure.in
===================================================================
--- configure.in	(revision 314182)
+++ configure.in	(working copy)
@@ -383,6 +383,7 @@
 dnl paths to the targets are relative to the build directory
 SAPI_SHARED=libs/libphp[]$PHP_MAJOR_VERSION[.]$SHLIB_DL_SUFFIX_NAME
 SAPI_STATIC=libs/libphp[]$PHP_MAJOR_VERSION[.a]
+SAPI_DYLIB=libs/libphp[]$PHP_MAJOR_VERSION[.]$SHLIB_SUFFIX_NAME
 SAPI_LIBTOOL=libphp[]$PHP_MAJOR_VERSION[.la]

 PHP_CONFIGURE_PART(Configuring SAPI modules)
Index: acinclude.m4
===================================================================
--- acinclude.m4	(revision 314182)
+++ acinclude.m4	(working copy)
@@ -798,6 +798,15 @@
 ])

 dnl
+dnl PHP_BUILD_DYLIB
+dnl
+AC_DEFUN([PHP_BUILD_DYLIB],[
+  PHP_BUILD_PROGRAM
+  OVERALL_TARGET=libphp[]$PHP_MAJOR_VERSION[.dylib]
+  php_sapi_module=dylib
+])
+
+dnl
 dnl PHP_BUILD_BUNDLE
 dnl
 AC_DEFUN([PHP_BUILD_BUNDLE],[
@@ -875,7 +884,7 @@
 dnl
 dnl PHP_SELECT_SAPI(name, type[, sources [, extra-cflags [, build-target]]])
 dnl
-dnl Selects the SAPI name and type (static, shared, bundle, program)
+dnl Selects the SAPI name and type (static, shared, bundle, program, dylib)
 dnl and optionally also the source-files for the SAPI-specific
 dnl objects.
 dnl
@@ -910,6 +919,7 @@
     static[)] PHP_BUILD_STATIC;;
     shared[)] PHP_BUILD_SHARED;;
     bundle[)] PHP_BUILD_BUNDLE;;
+    dylib[)] PHP_BUILD_DYLIB;;
     esac
     install_sapi="install-sapi"
     ifelse($3,,,[PHP_ADD_SOURCES([sapi/$1],[$3],[$4],[sapi])])

[/cce]
给php源码打上补丁之后,用./buildconf重新生成configure。在我这里高版本的autoconf会导致configure的时候出现很多错误,根本没法用,用port重新装了个autoconf213,并创建了一个autoconf的软链接,才解决了问题。在编译前看看你的Makefile中的OVERALL_TARGET,是不是libs/libphp5.dylib,如果不是,你的补丁打的有问题,或者修改Makefile,或者修改补丁文件。再看看有没有libs/libphp5.dylib的target。

注意:make的时候,它会忽略Makefile中的错误配置,直接略过。。。patch里的缩进可能有问题。除此以外看一下patch里的东西是不是都打入了源码

[cce code=”bash]
make -j 4 all
[/cce]

不出意外,你的目录下应该能看到libphp5.dylib了,直接copy到/usr/lib。再来试试make你的opdumper。编译后执行下看看,是不是很爽呢。

最终效果
[cce code=”bash”]
Script: echo.php
opnum line opcode op1 op2 result
0 2 ZEND_ECHO “helloworld”
1 3 ZEND_RETURN 1
[/cce]

发表评论