Как стать автором
Обновить

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

Время на прочтение7 мин
Количество просмотров3.4K

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


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


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


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


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


Для проверки 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.pro/platform/branches/radix-1.8

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


NO_CCACHE=1 make

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


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


Мы постарались максимально использовать возможности NGINX для создания простого HTTP-сервера. Вы можете задать другие настройки, например отключив использование Legacy библиотеки GeoIP и/или ассинхронных файловых операций (--with-file-aio), о нюансах которых, в системе на базе ядра Linux, можно почитать в статье Делаем асинхронность асинхронной, разбираемся в планировщике Go, ругаем Linux.



Теги:
Хабы:
Всего голосов 6: ↑5 и ↓1+6
Комментарии0

Публикации

Истории

Ближайшие события

25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань