Running php 5.2 fpm on ARM processor
Sunday, March 20th, 2016 10:06 pm GMT +2

Sometimes you need to run legacy php code on ARM processors. Since php 5.2 was released 2 November 2006 at the time there were not a lot of powerful ARM solutions capable of running full-fledged websites. However, right now there is a possibility to run pretty heavy code on modern ARM platforms and a lot of linux distros support ARM architecture. There is even dedicated ARM cloud hosting.

In this post we’re going to fix the inability to run PHP in the FPM(Fast Process Manager) mode which is an absolute requirement if you want to run production websites.

Compiling from sources

Since there is no old php releases in modern linux distros we’re obviously going to compile everything from scratch. For our purposes we’ll use ubuntu 15.04, let’s download php sources from PHP museum:

$ mkdir -p /mnt/build/ && cd /mnt/build/
$ wget http://museum.php.net/php5/php-5.2.17.tar.gz && tar -xf php-5.2.17.tar.gz
$ apt-get install -y build-essential nano wget curl libbz2-dev libncurses5-dev autoconf libxml2-dev

PHP-FPM patch

For historical reasons, Fast Process Manager(FPM) for 5.2.X branch exists only in the form of patch. Let’s download this patch and apply it the most recent 5.2 version:

$ cd php-5.2.17/
$ wget http://php-fpm.org/downloads/php-5.2.17-fpm-0.5.14.diff.gz && gzip -d php-5.2.17-fpm-0.5.14.diff.gz
$ patch -p1 < php-5.2.17-fpm-0.5.14.diff
 patching file configure
Hunk #7 succeeded at 110645 (offset 1324 lines).
Hunk #8 succeeded at 118930 (offset 1324 lines).
Hunk #9 succeeded at 119634 (offset 1324 lines).
Hunk #10 succeeded at 119689 (offset 1324 lines).
patching file configure.in
patching file libevent/ChangeLog
patching file libevent/Makefile.am
patching file libevent/Makefile.in
patching file libevent/README
patching file libevent/aclocal.m4

After this operation --enable-fpm option becomes available in ./configure script:

$ ./configure --help | grep "fpm"
  --enable-fpm              FastCGI: If this is enabled, the fastcgi support
  --with-fpm-conf=PATH        Set the path for php-fpm configuration file [PREFIX/etc/php-fpm.conf]
  --with-fpm-log=PATH         Set the path for php-fpm log file [PREFIX/logs/php-fpm.log]
  --with-fpm-pid=PATH         Set the path for php-fpm pid file [PREFIX/logs/php-fpm.pid]

Let’s try to compile php with FPM support:

$ ./configure --prefix=/opt/php52 --enable-fastcgi --enable-fpm

loading cache ./config.cache
checking for Cygwin environment... no
checking for mingw32 environment... no
checking for egrep... grep -E
checking for a sed that does not truncate output... /bin/sed
checking host system type... armv7l-unknown-linux-gnueabi
checking target system type... armv7l-unknown-linux-gnueabi
checking for gcc... gcc
checking whether the C compiler (gcc  ) works... yes
checking whether the C compiler (gcc  ) is a cross-compiler... no
checking whether we are using GNU C... yes
....

Thank you for using PHP.

So far, everything is looking fine. Let’s try to build it by running make using 4 ARM cores to speedup process:

$ make -j4

/bin/sh /mnt/build/php-5.2.17/libtool --silent --preserve-dup-deps --mode=compile gcc -Iext/date/lib -Iext/date/ -I/mnt/build/php-5.2.17/ext/date/ -DPHP_ATOM_
INC -I/mnt/build/php-5.2.17/include -I/mnt/build/php-5.2.17/main -I/mnt/build/php-5.2.17 -I/mnt/build/php-5.2.17/ext/date/lib -I/usr/include/libxml2 -I/mnt/bu
ild/php-5.2.17/TSRM -I/mnt/build/php-5.2.17/Zend    -I/usr/include -g -O2  -c /mnt/build/php-5.2.17/ext/date/php_date.c -o ext/date/php_date.lo
/bin/sh /mnt/build/php-5.2.17/libtool --silent --preserve-dup-deps --mode=compile gcc -Iext/date/lib -Iext/date/ -I/mnt/build/php-5.2.17/ext/date/ -DPHP_ATOM_
INC -I/mnt/build/php-5.2.17/include -I/mnt/build/php-5.2.17/main -I/mnt/build/php-5.2.17 -I/mnt/build/php-5.2.17/ext/date/lib -I/usr/include/libxml2 -I/mnt/bu
ild/php-5.2.17/TSRM -I/mnt/build/php-5.2.17/Zend    -I/usr/include -g -O2  -c /mnt/build/php-5.2.17/ext/date/lib/astro.c -o ext/date/lib/astro.lo

..........

/mnt/build/php-5.2.17/ext/dom/node.c: In function ‘dom_canonicalization’:
/mnt/build/php-5.2.17/ext/dom/node.c:1953:21: error: dereferencing pointer to incomplete type ‘xmlBuf {aka struct _xmlBuf}’
    ret = buf->buffer->use;
                     ^
Makefile:553: recipe for target 'ext/dom/node.lo' failed
make: *** [ext/dom/node.lo] Error 1

libxml2 patching

And boom! We’re stuck with the first error: dereferencing pointer to incomplete type xmlBuf {aka struct _xmlBuf}. After googling around, the problem seems to be related to libxml2 incompatibility and is well documented. Let’s fix that error by applying yet another patch:

$ wget https://github.com/bananos/dockerized/raw/master/armbuild-php52/patches/libxml29_compat.patch
$ patch -p1 < libxml29_compat.patch
patching file ext/dom/node.c
Hunk #1 succeeded at 1950 (offset 55 lines).
patching file ext/dom/documenttype.c
Hunk #1 succeeded at 215 (offset 10 lines).
patching file ext/simplexml/simplexml.c
Hunk #1 succeeded at 1343 (offset -74 lines).

And run make once again:

$ make -j4

......

In file included from /mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_shm_slots.h:8:0,
                 from /mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_children.c:28:
/mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_atomic.h:62:2: error: #error unsupported processor. please write a patch and send it to me
 #error unsupported processor. please write a patch and send it to me
  ^
/mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_atomic.h:66:32: error: unknown type name ‘atomic_t’
 static inline int fpm_spinlock(atomic_t *lock, int try_once)
                                ^
In file included from /mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_children.c:28:0:
/mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_shm_slots.h:16:3: error: unknown type name ‘atomic_t’
   atomic_t lock;
   ^
In file included from /mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_shm_slots.h:8:0,
                 from /mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_worker_pool.c:15:
/mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_atomic.h:62:2: error: #error unsupported processor. please write a patch and send it to me
 #error unsupported processor. please write a patch and send it to me
  ^
/mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_atomic.h:66:32: error: unknown type name ‘atomic_t’
 static inline int fpm_spinlock(atomic_t *lock, int try_once)
                                ^
In file included from /mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_worker_pool.c:15:0:
/mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_shm_slots.h:16:3: error: unknown type name ‘atomic_t’
   atomic_t lock;
   ^
Makefile:433: recipe for target 'sapi/cgi/fpm/fpm_worker_pool.lo' failed
make: *** [sapi/cgi/fpm/fpm_worker_pool.lo] Error 1
make: *** Waiting for unfinished jobs....
Makefile:431: recipe for target 'sapi/cgi/fpm/fpm_children.lo' failed
make: *** [sapi/cgi/fpm/fpm_children.lo] Error 1

Well, this is embarrassing. As we can see from compiler logs, the FPM author suggests us to write a patch and send it to him because current CPU type is not supported. Obviously, the first thing I did was to try google yet another patch to solve this problem, but unfortunately, there is no one — people moved on to newer PHP versions and no one cared enough to write a fix.

Therefore, we’re going to write our own patch. Let’s take a look at /mnt/build/php-5.2.17/sapi/cgi/fpm/fpm_atomic.h:62:2



  /* $Id: fpm_atomic.h,v 1.3 2008/09/18 23:34:11 anight Exp $ */
  /* (c) 2007,2008 Andrei Nigmatulin */

#ifndef FPM_ATOMIC_H
#define FPM_ATOMIC_H 1

#include <stdint.h>
#include <sched .h>

#if ( __i386__ || __i386 )

typedef int32_t                     atomic_int_t;
typedef uint32_t                    atomic_uint_t;
typedef volatile atomic_uint_t      atomic_t;

static inline atomic_int_t atomic_fetch_add(atomic_t *value, atomic_int_t add)
{
        __asm__ volatile ( "lock;" "xaddl %0, %1;" :
                "+r" (add) : "m" (*value) : "memory");

        return add;
}

static inline atomic_uint_t atomic_cmp_set(atomic_t *lock, atomic_uint_t old, atomic_uint_t set)
{
        unsigned char res;

        __asm__ volatile ( "lock;" "cmpxchgl %3, %1;" "sete %0;" :
                "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "memory");

    return res;
}

#elif ( __amd64__ || __amd64 )

// definitions of the same functions for AMD64

#else

#error unsupported processor. please write a patch and send it to me

#endif

Atomic types

Looks like we need to define those 2 functions for ARM platform. But how do we do it without proper knowledge of C and specifics of armhf architecture? Luckily, we may steal some code from newer PHP versions, which do work in FPM mode on ARM platform. Go to https://github.com/php/php-src/blob/PHP-5.3/sapi/fpm/fpm/fpm_atomic.h and take a look at source code:



    /* $Id: fpm_atomic.h,v 1.3 2008/09/18 23:34:11 anight Exp $ */
    /* (c) 2007,2008 Andrei Nigmatulin */

#ifndef FPM_ATOMIC_H
#define FPM_ATOMIC_H 1

#if HAVE_INTTYPES_H
# include <inttypes .h>
#else
# include <stdint .h>
#endif
#include <sched .h>

#ifdef HAVE_BUILTIN_ATOMIC

/**
 * all the cases below (as provided by upstream) define:
 * word as atomic_int_t, and
 * unsigned word as atomic_uint_t
 * and only use volatile atomic_uint_t as atomic_t
 */

typedef volatile unsigned long atomic_t;
#define atomic_cmp_set(a,b,c) __sync_bool_compare_and_swap(a,b,c)

#elif ( __i386__ || __i386 )

///  a lot of platform-speicific definitions of above mentioned functions

#if (__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))

#elif ( __arm__ || __arm ) /* W-Mark Kubacki */

///  other code we're not interested in

Although, it does contain the mention of ARM, the code defined for this case won’t be executed, since this is not our case:

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabihf/5/lto-wrapper
Target: arm-linux-gnueabihf
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.2.1-22ubuntu2' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libitm --disable-libquadmath --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-armhf/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-armhf --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-armhf --with-arch-directory=arm --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --enable-multilib --disable-sjlj-exceptions --with-arch=armv7-a --with-fpu=vfpv3-d16 --with-float=hard --with-mode=thumb --disable-werror --enable-multilib --enable-checking=release --build=arm-linux-gnueabihf --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf
Thread model: posix
gcc version 5.2.1 20151010 (Ubuntu 5.2.1-22ubuntu2)

What we’re going to do is leveraging built-in atomic types, which are controlled by HAVE_BUILTIN_ATOMIC flag. But first, let’s do a drop-in replacement of fpm_atomic.h from 5.3 branch:

$ cd /mnt/build/php-5.2.17/sapi/cgi/fpm
$ rm fpm_atomic.h && wget "https://github.com/php/php-src/raw/PHP-5.3/sapi/fpm/fpm/fpm_atomic.h"

Another magic step is in fact, fix configure system to be able to enable above mentioned flag. Open /mnt/build/php-5.2.17/sapi/cgi/fpm/acinclude.m4 and add at the top:

AC_DEFUN([AC_FPM_BUILTIN_ATOMIC],
[
  AC_MSG_CHECKING([if gcc supports __sync_bool_compare_and_swap])
  AC_TRY_LINK(,
  [
    int variable = 1;
    return (__sync_bool_compare_and_swap(&variable, 1, 2)
           && __sync_add_and_fetch(&variable, 1)) ? 1 : 0;
  ],
  [
    AC_MSG_RESULT([yes])
    AC_DEFINE(HAVE_BUILTIN_ATOMIC, 1, [Define to 1 if gcc supports __sync_bool_compare_and_swap() a.o.])
  ],
  [
    AC_MSG_RESULT([no])
  ])
])

After this is done, everything should compile just fine.

TL;DR

For those lazy folks who don’t want all the investigation details here’s an updated patch which I prepared:
https://github.com/bananos/dockerized/blob/master/armbuild-php52/patches/php-5.2.17-fpm-arm-0.5.14.diff

In order to use it, just download vanilla php-5.2.17 sources and apply the patch:

mkdir -p /tmp/build
cd /tmp/build && wget http://museum.php.net/php5/php-5.2.17.tar.gz && tar -xf php-5.2.17.tar.gz
cd /tmp/build && wget https://github.com/bananos/dockerized/raw/master/armbuild-php52/patches/php-5.2.17-fpm-arm-0.5.14.diff
cd /tmp/build && patch -p1 -d php-5.2.17/ < /tmp/build/php-5.2.17-fpm-arm-0.5.14.diff

If you’re familiar with Docker you may might also check precompiled and flattened PHP-5.2.17 ARMHF image
with some most popular PECL packages pre-built.

Happy running your old legacy PHP code!

Sources

http://php-fpm.org/

https://github.com/bananos/dockerized/tree/master/armbuild-php52/

https://github.com/php/php-src/

https://code.google.com/archive/p/php52-backports/issues/16

http://saravananlinux.blogspot.com/2014/01/libxml-error-while-compiling-dual-php.html