Новости и статьи

Среда разработки встроенных систем или дистрибутивов на основе GNU/Linux. Здесь вы можете найти новости проекта, статьи и заметки, касающиеся разработки дистрибутива Radix cross Linux.

Кросс-компиляция NGINX (для случая GCC)

1 декабря 2024 г.

NGINX – это HTTP‑сервер и обратный прокси‑сервер, почтовый прокси‑сервер, а также TCP/UDP прокси‑сервер общего назначения, изначально написанный Игорем Сысоевым. Уже длительное время он обслуживает серверы многих высоконагруженных сайтов.

NGINX

Однако кросс‑компиляция NGINX практически невозможна, поскольку разработанные Игорем Сысоевым скрипты конфигурирования исходного кода в большинстве случаев используют так называемую процедуру "Try Run".

Те кто знаком с утилитами Autoconf, Automake знают, что проверки необходимых параметров системы и кросс‑компилятора осуществляются различными процедурами, которые, в свою очередь, могут применять попытки сборки исходного кода (Try compile), компоновки объектных файлов (Try link) и, наконец, попытки запуска тестовых программ (Try Run).

Естественно, если речь идет о кросс‑сборке, операции "Try Run" недопустимы, ведь мы не можем запустить программу, собранную под целевую архитектуру отличающуюся от архитектуры машины сборки на самой машине сборки.

tags

В Autotools проблемы, связанные с невозможностью запуска целевых программ на сборочной машине в некоторых случаях решаются кешированием переменных, которые могу быть переопределены пользователем в файле ‑‑cache‑file или заданы в командной строке вызова скрипта ./configure.

Скрипты Игоря Сысоева не предусматривают такого переопределения машинно‑зависимых величин. Однако величин, которые необходимо задать во время конфигурирования исходных кодов NGINX достаточно много. К ним, в первую очередь, относятся размеры типов данных. Именно с них мы и начнем.

Размеры переменных различных типов проверяются с помощью скрипта auto/types/sizeof. Тестовые процедуры возвращают размер в байтах и предаются с помощью переменной ngx_size. В случае использования gcc для вычисления основных размеров данных, мы можем воспользоваться предопределенными macro С‑компилятора, входящего в поставку GCC. Если мы добавим в каталог auto/types скрипт 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

и поправим скрипт auto/types/sizeof так, чтобы в случае GCC, для вычисления размеров вызывался скрипт auto/types/gcc‑sizeof, то на данном этапе конфигурирования мы сможем обеспечить правильную работу без запуска тестовых исполняемых файлов:

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 
 #include 
@@ -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

Для проверки big/little‑endian мы также можем воспользоваться предопределенными macro, добавив собственный скрипт gcc‑endianness в каталог 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

и немного поправив оригинальный скрипт 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

Здесь надо учитывать то, что скрипт endianness ожидает нормального выхода из скрипта gcc‑endianness в случае little‑endian и аварийного завершения в случае big‑endian.

Покончив с основными типами данных и ориентацией байтов, мы можем отменить проверку самого cross‑компилятора, просто запретив выполнение тестовой процедуры в файле 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=

Однако это еще не все. Далее нам необходимо обеспечить проверки, осуществляемые с помощью скрипта auto/feature. Здесь вам будет необходимо, самостоятельно собрав и выполнив код некоторых тестовых программ, разобраться в том, какие из проверок вы сможете заменить на заготовленные вами ответы. Ответы передаются посредством переменной ngx_found. Если речь идет об обычном дистрибутиве на базе ядра Linux и GNU Libc, то вам будет достаточно следующих изменений файла 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)

Готовый patch для NGINX версии 1.20.2 можно получить следующим образом. Необходимо загрузить исходный код Radix cross Linux с помощью команды:

svn checkout svn://radix-linux.su/radix/system/branches/radix-1.9

Сменить каталог на radix-1.9/sources/packages/n/nginx/ и выполнить команду make:

NO_CCACHE=1 make

Таким образом вы получите исходный архив NGINX и необходимый для кросс‑сборки patch в каталоге patches.

Весть процесс сборки NGINX можно видеть в каталоге radix-1.9/net/nginx/1.20.2. Здесь в Make‑файле представлены основные параметры конфигурирования для различных архитектур.

Сервер Angie пока имеет аналогичный способ конфигурирования исходного кода перед сборкой. Надеемся, данная статья пригодится тем, кто собирает и инсталлирует Angie в образы своих дистрибутивов.

Контакты: