Cross compiling NGINX (for the GCC case)

nginx is a HTTP server and reverse proxy, mail proxy, and general purpose TCP/UDP proxy originally written by Igor Sysoev. It has been serving the servers of many highly loaded sites for a long time.

However, cross-compilation nginx is practically impossible, since the source code configuration scripts developed by Igor Sysoev in most cases use the so-called “Try Run” procedure.

Those who are familiar with utilities Autoconf, automake know that checks of the necessary system and cross-compiler parameters are carried out by various procedures, which, in turn, can use attempts to compile the source code (Try compile), link object files (Try link) and, finally, attempts to run test programs (Try Run ).

Naturally, when it comes to cross-assembly, “Try Run” operations are not allowed, because we cannot run a program built for a target architecture different from the architecture of the build machine on the build machine itself.

V Autotools problems associated with the impossibility of launching target programs on the assembly machine in some cases are solved by caching variables that can be redefined by the user in the file –cache-file or set on the command line of the script call ./configure.

Igor Sysoev’s scripts do not provide for such a redefinition of machine-dependent values. However, the values ​​that must be set when configuring the source codes nginx enough. These primarily include the sizes of data types. It is with them that we will begin.

Sizes of variables of various types are checked using a script auto/types/sizeof. Test procedures return the size in bytes and are passed using a variable ngx_size. In case of use gcc to calculate the basic data sizes, we can use the predefined macros of the C compiler included in the delivery GCC. If we add to the directory auto/types script gcc-sizeof:

#!/bin/sh

if [ "x$NGX_CC_NAME" = "x" -o "x$ngx_type" = "x" ] ; then
  echo "unknown"
fi

if [ "$ngx_type" = "int" ] ; then
  size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_INT__ | cut -f3 -d' ' | sed 's,^[ t]*,,' | sed 's,[ t]*$,,'`
  echo "$size_from_cpp"
elif [ "$ngx_type" = "long" ] ; then
  size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_LONG__ | cut -f3 -d' ' | sed 's,^[ t]*,,' | sed 's,[ t]*$,,'`
  echo "$size_from_cpp"
elif [ "$ngx_type" = "long long" ] ; then
  size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_LONG_LONG__ | cut -f3 -d' ' | sed 's,^[ t]*,,' | sed 's,[ t]*$,,'`
  echo "$size_from_cpp"
elif [ "$ngx_type" = "size_t" ] ; then
  size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_SIZE_T__ | cut -f3 -d' ' | sed 's,^[ t]*,,' | sed 's,[ t]*$,,'`
  echo "$size_from_cpp"
elif [ "$ngx_type" = "sig_atomic_t" ] ; then
  size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_INT__ | cut -f3 -d' ' | sed 's,^[ t]*,,' | sed 's,[ t]*$,,'`
  echo "$size_from_cpp"
elif [ "$ngx_type" = "void *" ] ; then
  size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_POINTER__ | cut -f3 -d' ' | sed 's,^[ t]*,,' | sed 's,[ t]*$,,'`
  echo "$size_from_cpp"
elif [ "$ngx_type" = "off_t" ] ; then
  size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_PTRDIFF_T__ | cut -f3 -d' ' | sed 's,^[ t]*,,' | sed 's,[ t]*$,,'`
  echo "$size_from_cpp"
elif [ "$ngx_type" = "time_t" ] ; then
  size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_PTRDIFF_T__ | cut -f3 -d' ' | sed 's,^[ t]*,,' | sed 's,[ t]*$,,'`
  echo "$size_from_cpp"
fi

and fix the script auto/types/sizeof so that in case GCCa script was called to calculate the dimensions auto/types/gcc-sizeofthen at this stage of configuration we will be able to ensure correct operation without running test executable files:

diff -b --unified -Nr nginx-1.20.2-orig/auto/types/sizeof nginx-1.20.2/auto/types/sizeof
--- nginx-1.20.2-orig/auto/types/sizeof	2021-11-16 17:44:02.000000000 +0300
+++ nginx-1.20.2/auto/types/sizeof	2022-02-13 19:50:26.816530942 +0300
@@ -14,7 +14,8 @@
 
 ngx_size=
 
-cat << END > $NGX_AUTOTEST.c
+if [ "$NGX_CC_NAME" != "gcc" ] ; then
+  cat << END > $NGX_AUTOTEST.c
 
 #include <sys/types.h>
 #include <sys/time.h>
@@ -33,18 +34,21 @@
 END
 
 
-ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS 
+  ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS 
           -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs"
 
-eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
+  eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
 
 
-if [ -x $NGX_AUTOTEST ]; then
+  if [ -x $NGX_AUTOTEST ]; then
     ngx_size=`$NGX_AUTOTEST`
     echo " $ngx_size bytes"
+  fi
+else
+    ngx_size=`ngx_type="$ngx_type" . auto/types/gcc-sizeof`
+    echo " $ngx_size bytes"
 fi
 
-
 case $ngx_size in
     4)
         ngx_max_value=2147483647

To test big/little endian, we can also use the predefined macros by adding our own script gcc-endianness to catalog auto:

#!/bin/sh

if [ "x$NGX_CC_NAME" = "x" ] ; then
  exit 0
fi

if `${CC} -dM -E - < /dev/null | grep " __BYTE_ORDER__ " | cut -f3 -d' ' | grep -q "_BIG_"` ; then
  exit 1
fi

and slightly correcting the original script endianness:

diff -b --unified -Nr nginx-1.20.2-orig/auto/endianness nginx-1.20.2/auto/endianness
--- nginx-1.20.2-orig/auto/endianness	2021-11-16 17:44:02.000000000 +0300
+++ nginx-1.20.2/auto/endianness	2022-02-13 19:50:26.816530942 +0300
@@ -13,7 +13,8 @@
 END
 
 
-cat << END > $NGX_AUTOTEST.c
+if [ "$NGX_CC_NAME" != "gcc" ] ; then
+  cat << END > $NGX_AUTOTEST.c
 
 int main(void) {
     int i = 0x11223344;
@@ -26,12 +27,12 @@
 
 END
 
-ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS 
+  ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS 
           -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs"
 
-eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
+  eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
 
-if [ -x $NGX_AUTOTEST ]; then
+  if [ -x $NGX_AUTOTEST ]; then
     if $NGX_AUTOTEST >/dev/null 2>&1; then
         echo " little endian"
         have=NGX_HAVE_LITTLE_ENDIAN . auto/have
@@ -41,10 +42,18 @@
 
     rm -rf $NGX_AUTOTEST*
 
-else
+  else
     rm -rf $NGX_AUTOTEST*
 
     echo
     echo "$0: error: cannot detect system byte ordering"
     exit 1
+  fi
+else
+  if `. auto/gcc-endianness` ; then
+      echo " little endian"
+      have=NGX_HAVE_LITTLE_ENDIAN . auto/have
+  else
+      echo " big endian"
+  fi
 fi

Here it is necessary to take into account that the script endianness waiting for a normal exit from the script gcc-endianness in case of little-endian and crash in case of big-endian.

Having done with the basic data types and byte orientation, we can override the check of the cross compiler itself by simply forbidding the execution of the test procedure in the file auto/cc/name:

diff -b --unified -Nr nginx-1.20.2-orig/auto/cc/name nginx-1.20.2/auto/cc/name
--- nginx-1.20.2-orig/auto/cc/name	2021-11-16 17:44:02.000000000 +0300
+++ nginx-1.20.2/auto/cc/name	2022-02-13 19:50:26.816530942 +0300
@@ -7,7 +7,7 @@
 
     ngx_feature="C compiler"
     ngx_feature_name=
-    ngx_feature_run=yes
+    ngx_feature_run=
     ngx_feature_incs=
     ngx_feature_path=
     ngx_feature_libs=

However, that’s not all. Next, we need to provide checks that are carried out using a script. auto/feature. Here you will need to independently assemble and execute the code of some test programs and figure out which of the checks you can replace with the answers you have prepared. Responses are passed through a variable ngx_found. If we are talking about a regular kernel-based distribution linux and GNU libcthen the following changes to the file will be enough for you auto/feature:

diff -b --unified -Nr nginx-1.20.2-orig/auto/feature nginx-1.20.2/auto/feature
--- nginx-1.20.2-orig/auto/feature	2021-11-16 17:44:02.000000000 +0300
+++ nginx-1.20.2/auto/feature	2022-02-13 19:50:26.816530942 +0300
@@ -52,6 +52,85 @@
     case "$ngx_feature_run" in
 
         yes)
+
+            if [ "$ngx_feature_name" = "NGX_HAVE_GCC_ATOMIC" ] ; then
+                echo " found"
+                ngx_found=yes
+
+                if test -n "$ngx_feature_name"; then
+                    have=$ngx_have_feature . auto/have
+                fi
+            elif [ "$ngx_feature_name" = "NGX_HAVE_C99_VARIADIC_MACROS" ] ; then
+                echo " found"
+                ngx_found=yes
+
+                if test -n "$ngx_feature_name"; then
+                    have=$ngx_have_feature . auto/have
+                fi
+            elif [ "$ngx_feature_name" = "NGX_HAVE_GCC_VARIADIC_MACROS" ] ; then
+                echo " found"
+                ngx_found=yes
+
+                if test -n "$ngx_feature_name"; then
+                    have=$ngx_have_feature . auto/have
+                fi
+            elif [ "$ngx_feature_name" = "NGX_HAVE_EPOLL" ] ; then
+                echo " found"
+                ngx_found=yes
+
+                if test -n "$ngx_feature_name"; then
+                    have=$ngx_have_feature . auto/have
+                fi
+            elif [ "$ngx_feature_name" = "NGX_HAVE_SENDFILE" ] ; then
+                echo " found"
+                ngx_found=yes
+
+                if test -n "$ngx_feature_name"; then
+                    have=$ngx_have_feature . auto/have
+                fi
+            elif [ "$ngx_feature_name" = "NGX_HAVE_SENDFILE64" ] ; then
+                echo " found"
+                ngx_found=yes
+
+                if test -n "$ngx_feature_name"; then
+                    have=$ngx_have_feature . auto/have
+                fi
+            elif [ "$ngx_feature_name" = "NGX_HAVE_PR_SET_DUMPABLE" ] ; then
+                echo " found"
+                ngx_found=yes
+
+                if test -n "$ngx_feature_name"; then
+                    have=$ngx_have_feature . auto/have
+                fi
+            elif [ "$ngx_feature_name" = "NGX_HAVE_PR_SET_KEEPCAPS" ] ; then
+                echo " found"
+                ngx_found=yes
+
+                if test -n "$ngx_feature_name"; then
+                    have=$ngx_have_feature . auto/have
+                fi
+            elif [ "$ngx_feature_name" = "NGX_HAVE_MAP_ANON" ] ; then
+                echo " found"
+                ngx_found=yes
+
+                if test -n "$ngx_feature_name"; then
+                    have=$ngx_have_feature . auto/have
+                fi
+            elif [ "$ngx_feature_name" = "NGX_HAVE_MAP_DEVZERO" ] ; then
+                echo " found"
+                ngx_found=yes
+
+                if test -n "$ngx_feature_name"; then
+                    have=$ngx_have_feature . auto/have
+                fi
+            elif [ "$ngx_feature_name" = "NGX_HAVE_SYSVSHM" ] ; then
+                echo " found"
+                ngx_found=yes
+
+                if test -n "$ngx_feature_name"; then
+                    have=$ngx_have_feature . auto/have
+                fi
+            else
             # /bin/sh is used to intercept "Killed" or "Abort trap" messages
             if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
                 echo " found"
@@ -64,6 +143,8 @@
             else
                 echo " found but is not working"
             fi
+            fi
+
         ;;
 
         value)

Ready patch for nginx versions 1.20.2 can be obtained as follows. You need to get the source code Radix cross Linux using the command:

svn checkout svn://radix.pro/platform/branches/radix-1.8

Change directory to radix-1.8/sources/packages/n/nginx and run the command make:

NO_CCACHE=1 make

This way you will get the original archive nginx and necessary for cross-assembly patch in the directory patches.

Know the build process nginx can be seen in the catalog radix-1.8/net/nginx/1.20.2. Here in the Makefile are the basic configuration options for various architectures.

We have tried to make the most of the possibilities. nginx to create a simple HTTP server. You can specify other settings, for example by disabling the use of the Legacy GeoIP library and / or asynchronous file operations (–with-file-aio), the nuances of which, in a kernel-based system linuxyou can read in the article Making asynchrony asynchronous, we understand the Go scheduler, we scold Linux.

Similar Posts

Leave a Reply