MIPS, ELF и частичное связывание

У меня есть большой программный проект со сложным процессом сборки, который работает следующим образом:

  1. Скомпилируйте отдельные исходные файлы.
  2. Частично свяжите объектные файлы для каждого модуля в другой .o, используя ld -r.
  3. Скрыть приватные символы в каждом модуле с помощью objcopy -G.
  4. Частично свяжите объекты модуля вместе, снова используя ld -r.
  5. Связывайте модули вместе в общий объект.

Шаг 3 необходим, чтобы разрешить частные глобальные переменные модуля, которые не экспортируются в остальную часть проекта.

Все это прекрасно работает с ARM и IA32. К сожалению, теперь мне нужно заставить все работать на mips (в частности, mipsel-linux-gnu для Android). А общий объектный ABI MIPS значительно сложнее, чем на других платформах, и он не работает.

Что происходит, так это то, что шаг 5 завершается с ошибкой:

CALL16 reloc at 0x1234 not against global symbol

Похоже, это связано с тем, что компилятор генерирует перемещения CALL16 для вызова функций в другой единице компиляции, но CALL16 позволяет вам вызывать только глобальные символы --- и из-за шага 3 некоторые из символов, которые мы пытаемся вызвать, не являются глобально больше.

На данный момент я вижу несколько возможных вариантов:

  • убедить компоновщика разрешить перемещения CALL16 в обычные вызовы, относящиеся к ПК внутри модуля компиляции, на шаге 2.
  • то же самое, но на шаге 4 или 5.
  • сообщить компилятору, чтобы он не генерировал перемещения CALL16 для вызовов функций между модулями компиляции.
  • Другие.

Отключение шага 3, боюсь, не вариант из-за внешних требований.

Что я действительно хотел бы сделать, так это сгенерировать абсолютный код, который во время загрузки исправляется по нужным адресам; он меньше, намного быстрее и значительно проще, и нам не нужно совместно использовать библиотеку между процессами. К сожалению, похоже, что Android dlopen() не поддерживает это.

В настоящее время я не в своей тарелке. У кого-нибудь есть предложения?

Это gcc 4.4.5 (от Emdebian), binutils 2.20.1. Целью BFD является elf32-tradlittlemips. Хост-ОС — это Linux, и я выполняю кросс-компиляцию для Android.

Дополнение

Я также получаю подобные предупреждения с шага 4.

$MODULE.o: Can't find matching LO16 reloc against `$SYMBOLNAME' for R_MIPS_GOT16 at 0x18 in section `.text.$SYMBOLNAME'

Глядя на дизассемблирование входных данных для шага 4, я вижу, что компилятор сгенерировал такой код:

50:   8f9e0000        lw      s8,0(gp)
                      50: R_MIPS_GOT16        $SYMBOLNAME
54:   8fd9001c        lw      t9,28(s8)
58:   0320f809        jalr    t9
5c:   00a02021        move    a0,a1

Разве GOT16 не фиксируется до старшей половины адреса, и за ним должен следовать LO16 для младшей половины? Но код выглядит так, как будто он пытается выполнить косвенное обращение GOT. Это меня озадачивает. Я понятия не имею, связано ли это с моей предыдущей проблемой, или это другая проблема, или это вообще не проблема...

Обновить

Очевидно, MIPS просто не поддерживает скрытые глобальные символы!

Мы обошли это, исказив имена символов, которые должны быть скрыты, чтобы никто не мог сказать, что они из себя представляют. Это довольно сильно подталкивает внешние требования, но я убедил руководство, указав, что это единственный способ получить поставляемый продукт.

Это совершенно ужасно (и включает в себя очень отвратительную работу с make-файлом), поэтому я бы предпочел лучшее решение, если у кого-то оно есть...


person David Given    schedule 11.11.2011    source источник


Ответы (2)


Я не уверен в конкретных проблемах GOT, с которыми вы сталкиваетесь. Есть много ошибок и проблем с GOT, LO16/HI16 в binutils. Я думаю, что большинство из них было исправлено в версии, которую вы используете, если только вы не нацелены на MIPS16 (чего вы, похоже, не делаете). LO16 действительно необходим только там, за пределами MIPS16 вы извлекаете полное 26-битное смещение из GOT, поскольку у вас есть 32-битные регистры. LO16 не нужен, но по-прежнему формально требуется некоторыми ABI/API, но он был сфальсифицирован как самое большее предупреждение (вы можете попробовать удалить -Werror на этом этапе, если вы его используете). Честно говоря, я понимаю только самые основы этой части, в остальной части вашей ситуации у меня были некоторые рекомендации, если не ответ (трудно быть уверенным, учитывая сложность вашей настройки).

В MIPS (и большинстве сборок, с которыми я знаком) у вас есть три основных уровня видимости: локальный, глобальный и слабый. Кроме того, у вас есть comm для общих объектов. GNU, конечно, любит усложнять и добавляет больше. газ бывает защищенным, скрытым и внутренним (минимально сложно уследить за всеми расширениями). При всем этом шаги, которые вы настраиваете вручную с видимостью, кажутся ненужными.

Если вы можете удалить промежуточную глобальность переменных, это должно устранить необходимость сделать их неглобальными, что может только упростить любые проблемы с GOT, с которыми вы столкнетесь позже.

Общие проблемы немного сбивают с толку. Я не уверен, что вы подразумеваете под скрытыми глобальными символами, это немного противоречие (конечно, переносимость и конкретные проекты дают сумасшедшие проблемы и ограничения). Кажется, вам нужны символы кросс-сборочных единиц на одном этапе, но не на более позднем этапе. Не используя расширения GNU (чего лучше избегать в моей книге), вы можете заменить глобальные переменные в шагах 1-2 на comm и/или weakglobals. Вы всегда можете использовать трюк с препроцессором, чтобы избежать наличия нескольких подблоков на этапе (уродливо, но это переносимый код на этом уровне).

У вас действительно есть установка 1) подмодулей 2) подмодулей -> модули 3-5) модулей -> общая библиотека. Упрощение не помешает. Вы всегда можете вставить в 2) или 3-5) интерфейс C-уровня, просто чтобы найти, какую сборку GCC будет производить для вашей архитектуры, и использовать ее в качестве основы для разделения видимости на чистые интерфейсы.

Хотел бы я дать вам индивидуальное решение, но это практически невозможно без вашего полного проекта для работы. Я могу заверить, что, хотя расположение MIPS (особенно цепочки инструментов) имеет проблемы, параметры видимости (особенно если вы используете газ, libbfd и gcc) одинаковы.

person D'Nabre    schedule 23.04.2012

ваш binutils слишком стар. некоторые наборы изменений в 2.23 могут решить вашу проблему, например, «скрыть символы без ссылок PLT или GOT».

person liaoxinglong    schedule 13.09.2013