Я пытаюсь скомпилировать программу prog
и связать ее с OpenSSL 1.0.2 beta, собранной из исходников и установленной в /usr/local/ssl-1.0.2
. На старой системе с 0.9.8 это работает без особых проблем. На более новой системе с установленной 1.0.1 это требует немного больше работы. Интересно, почему.
1) В Ubuntu 10.04 с OpenSSL 0.9.8:
Вот шаги, которые я выполняю для компиляции и линковки с версией 1.0.2.
$ ./config shared --openssldir=/usr/local/ssl-1.0.2 && make && make install
$ ldconfig
$ ldconfig -p | grep libcrypto
=> Отображаются только файлы 0.9.8, поэтому я добавляю путь к файлам 1.0.2...
$ ldconfig /usr/local/ssl-1.0.2/lib
$ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0
libcrypto.so.0.9.8 (libc6, hwcap: 0x0008000000008000) => /lib/i686/cmov/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6, hwcap: 0x0004000000000000) => /lib/i586/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6, hwcap: 0x0002000000000000) => /lib/i486/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6) => /lib/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6) => /usr/lib/libcrypto.so.0.9.8
libcrypto.so (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so
И поэтому я могу скомпилировать prog
...
$ gcc -o prog ... -L/usr/local/ssl-1.0.2/lib -lcrypto
$ ldd prog
=>
libcrypto.so.1.0.0 => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 (0x0083b000)
... и он правильно связан с 1.0.2.
2) В Debian Wheezy с OpenSSL 1.0.1:
Те же шаги, другой результат.
$ ./config shared --openssldir=/usr/local/ssl-1.0.2 && make && make install
$ ldconfig
$ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6, hwcap: 0x0008000000008000) => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6, hwcap: 0x0004000000000000) => /usr/lib/i386-linux-gnu/i586/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6) => /usr/lib/i386-linux-gnu/libcrypto.so.1.0.0
Аналогично добавляю путь к 1.0.2...
$ ldconfig /usr/local/ssl-1.0.2/lib
$ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6, hwcap: 0x0008000000008000) => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6, hwcap: 0x0004000000000000) => /usr/lib/i386-linux-gnu/i586/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6) => /usr/lib/i386-linux-gnu/libcrypto.so.1.0.0
libcrypto.so (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so
Затем я пытаюсь скомпилировать...
$ gcc -o prog ... -L/usr/local/ssl-1.0.2/lib -lcrypto
$ ldd prog
=>
libcrypto.so.1.0.0 => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0 (0xb7591000)
Но здесь он не связан с 1.0.2. Путь к библиотеке времени компиляции правильный (указанный с -L
, gcc
в противном случае не сработает, так как некоторые функции, используемые в , prog
специфичны для 1.0.2), но не путь времени выполнения.
3) Как заставить это работать на Wheezy
С запуском или без ldconfig /usr/local/ssl-1.0.2/lib
:
$ gcc -o prog ... -Wl,--rpath=/usr/local/ssl-1.0.2/lib -L/usr/local/ssl-1.0.2/lib -lcrypto
$ ldd prog
=>
libcrypto.so.1.0.0 => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 (0xb7592000)
Либо запустите export LD_LIBRARY_PATH=/usr/local/ssl-1.0.2/lib
перед запуском gcc
.
Что я хотел бы знать
Используя LD_DEBUG=libs ./prog
подсказку mr.spuratic, я обнаружил, что пути просматриваются в /etc/ld.so.cache
. Я открыл этот файл и обнаружил, что порядок, в котором просматриваются .so, соответствует выводу ldconfig -p
.
Итак, фактический вопрос:
- Почему файл 1.0.2 оказывается наверху списка ldconfig в 1), но не в 2)? Чистая случайность? Путаница из-за того, что файлы 1.0.1 и 1.0.2 имеют одинаковый суффикс? ("1.0.0")
Или, говоря по-другому,
- Почему флаги, добавленные в 3), не нужны в 1)?
решение1
При компиляции/линковке с пакетом, не являющимся пакетом по умолчанию, необходимо учитывать три момента:
- заголовки (обычно
CFLAGS
) - путь к библиотеке времени компиляции (обычно
LDFLAGS
) - путь к библиотеке времени выполнения (rpathчерез
LDFLAGS
,LD_RUN_PATH
,LD_LIBRARY_PATH
илиld.so.conf
)
Вы не сказали, что это prog
такое, поэтому я не могу сказать, насколько корректной может быть его конфигурация (или использует ли он autoconf?). Я видел много таких, которые надежно выполняют только первые два шага.
На этапе компоновки важен порядок путей к библиотекам. Если вы используете набор инструментов GNU (gcc и binutils), вы, вероятно, можете увидеть, что происходит, установив CFLAGS
заранее configure
(или, возможно, Makefile
непосредственно в файле):
export CFLAGS="-Wl,-t"
Это передает -t
опцию трассировки компоновщику. Вам может потребоваться добавить V=1
или VERBOSE=1
к команде make, если вы получаете только краткие строки "CC" и "LD" на выходе во время make.)
Во время выполнения вы можете увидеть, что ld.so
пытается сделать, тщательно настроив LD_DEBUG
, например
LD_DEBUG=libs ./myprog
(или попробуйте значения files
или symbols
для более подробной информации)
Чтобы правильно указать все три параметра во время сборки, вы должны иметь возможность сделать следующее:
export CFLAGS="-I/usr/local/ssl-1.0.2/include"
export LDFLAGS="-L/usr/local/ssl-1.0.2/lib -R/usr/local/ssl-1.0.2/lib"
затем перенастройте/перекомпилируйте.
Вы используете --openssldir
вместо более традиционного --prefix
(я рекомендую последний вариант, а также используйте его только make install_sw
если вам не нужны 1000 или около того страниц руководства и символических ссылок, которые дает вам установка по умолчанию). Это может быть частью проблемы. По какой-то причине библиотеки .so, которые вы показываете, как известно, ld.so
не имеют суффикса версии (например, .so.1.0.2
), правильный " make install
" должен был настроить это для вас (через link-shared
цель в main Makefile
).
Опция -R
указывает компоновщику встроить RPATH в исполняемый файл вывода для конкретной библиотеки OpenSSL, чтобы ему не приходилось полагаться на значение по умолчанию, которое ld.so
обычно предоставляет компоновщик времени выполнения ( ). Вы можете изменить существующие двоичные файлы с помощьюchrpath
вместо.
Это более или менее эквивалентно экспорту LD_LIBRARY_PATH=/usr/local/ssl-1.0.2/lib
. Подробнее о RPATH и связанном с ним RUNPATH можно прочитать здесь:http://blog.tremily.us/posts/rpath/
В крайнем случае вы можете собрать OpenSSL без «shared» или с «noshared», это даст вам статические библиотеки, у которых не будет этой проблемы (но могут быть другие проблемы, например, при использовании в ELF .so, что приведет к проблемам PIC/PIE)
На основе обновленных данных я полагаю, что проблема в том, что 1.0.1 и 1.0.2beta обе устанавливают суффикс версии .so (SONAME) на 1.0.0. В первой системе только с 0.9.8 это не вызывает проблем; во второй, где 1.0.1 и 1.0.2 обе версии имеют версию 1.0.0, "побеждает первое совпадение" на основе порядка ld.so.{conf,d}
. Помните, ld
компоновщик времени компиляции — это другая программа, чем ld.so
компоновщик времени выполнения, и может вести себя по-другому (обычно это приводит к ошибкам символов или к чему-то похуже, как вы видели).
$ cd /usr/local/src/openssl/openssl/1.0.2beta1
$ readelf -a libssl.so | grep SONAME
0x0000000e (SONAME) Library soname: [libssl.so.1.0.0]
$ cat verchk.c
int main(int argc, char *argv[]) {
printf("build: %s\n",OPENSSL_VERSION_TEXT);
printf("run : %s\n",SSLeay_version(SSLEAY_VERSION));
return 0;
}
$ gcc -Wall -I/usr/local/src/openssl/openssl-1.0.2-beta1/include \
-Wl,-rpath /usr/local/src/openssl/openssl-1.0.2-beta1/ \
-o verchk /usr/local/src/openssl/openssl-1.0.2-beta1/libcrypto.so verchk.c
$ ./verchk
build: OpenSSL 1.0.2-beta1 24 Feb 2014
run : OpenSSL 1.0.2-beta1 24 Feb 2014
$ grep SHLIB_M...R= Makefile
SHLIB_MAJOR=1
SHLIB_MINOR=0.0
Обновлять
В OpenSSL-1.1 внесены некоторые изменения на уровне API, приведенный выше код не будет скомпилирован с заголовками v1.1 и старыми библиотеками ( undefined reference to `OpenSSL_version'
).
SSLeay_version()
в настоящее время устарело и (в зависимости от OPENSSL_API_COMPAT
) может быть #define
заменено на соответствующую функцию API OpenSSL_version()
.
решение2
Путь к новой библиотеке находится в одном из файлов внутри /etc/ld.so.conf.d
? Следующий запуск:-
#ldconfig -v
для перестроения кэша. Если вы быстры, вы должны увидеть новую библиотеку в длинном списке, который она выводит (или передать ее по конвейеру grep
или less
)
Может быть, на первом сервере путь уже был, а на втором — нет?