Невозможно загрузить объект со статическим TLS

У меня есть приложение, которое использует dlopen() для загрузки дополнительных модулей. Приложение и модули построены на Ubuntu 12.04 x86_64 с использованием gcc 4.6, но для i386 arch. Затем двоичные файлы копируются на другую машину с точно такой же ОС и работают нормально.

Однако, если они скопированы в Ubuntu 12.04 i386, то некоторые (но не все) модули не загружаются со следующим сообщением:

dlopen: cannot load any more object with static TLS

Я подозреваю, что это вызвано использованием переменных __thread. Однако такие переменные не используются в загруженных модулях - только в самом модуле загрузчика.

Может ли кто-нибудь предоставить дополнительную информацию, какова может быть причина?

Я сокращаю число переменных __thread и оптимизирую их (с помощью -ftls-model и т.д.), мне просто интересно, почему он не работает почти в той же системе.

Ответ 1

Я подозреваю, что это вызвано использованием переменных __thread.

Правильно.

Однако такие переменные не используются в загруженных модулях - только в самом модуле загрузчика.

Неправильно. Вы не можете использовать __thread самостоятельно, но какая-то библиотека, которую вы статически связываете с модулем, использует их. Вы можете подтвердить это с помощью:

readelf -l /path/to/foo.so | grep TLS

В чем может быть причина?

Модуль использует -ftls-model=initial-exec, но должен использовать -ftls-model=global-dynamic. Это чаще всего происходит, когда (некоторые из) код, связанный с foo.so, строится без -fPIC.

Связывание кода non -fPIC в общую библиотеку невозможно на x86_64, но разрешено на ix86 (и приводит ко многим тонким проблемам, как этот).

Update:

У меня есть 1 модуль, скомпилированный без -fpIC, но я не устанавливаю tls-model вообще, насколько я помню, значение по умолчанию не является initial-exec

  • Для каждого изображения ELF (исполняемая или разделяемая библиотека) может быть только одна модель tls.
  • Модель TLS по умолчанию равна initial-exec для кода не -fPIC.

Из этого следует, что если вы связываете хотя бы один объект -fPIC, который использует __thread в foo.so, то foo.so получает initial-exec для всех своих TLS.

Так почему это вызывает проблемы - потому что, если используется initial-exec, количество переменных tls ограничено (потому что они не динамически распределены)?

Правильно.