Почему полоса OSX не может удалить слабые символы? - PullRequest
0 голосов
/ 30 августа 2018

При попытке вычеркнуть ненужные вещи из моего связанного исполняемого файла я обнаружил странную вещь. Предположим, у нас есть простая, простая программа на C ++:

class Foo {
public:
    template <typename T>
    char* getPtr() {
        static char c;
        return &c;
    }
};

char* bar() {
    Foo i;
    return i.getPtr<int>();
}

int main() {
    bar();
} 

Двоичный файл, создаваемый с помощью clang++ t.cc, имеет следующую динамическую таблицу символов:

$ gobjdump -T ./a.out

./a.out:     file format mach-o-x86-64

DYNAMIC SYMBOL TABLE:
0000000100000f40 g       0f SECT   01 0000 [.text] __Z3barv
0000000100000f60 g       0f SECT   01 0080 [.text] __ZN3Foo6getPtrIiEEPcv
0000000100001020 g       0f SECT   08 0080 [.data] __ZZN3Foo6getPtrIiEEPcvE1c
0000000100000000 g       0f SECT   01 0010 [.text] __mh_execute_header
0000000100000f80 g       0f SECT   01 0000 [.text] _main
0000000000000000 g       01 UND    00 0200 dyld_stub_binder

Учитывая, что это исполняемый файл, а не dylib, я бы хотел удалить все записи, кроме тех, которые указаны для неопределенных символов. Теоретически двоичный файл все еще будет работать, поскольку информация о требуемой привязке dyld все еще там, и точка входа определяется в некоторой команде загрузки после заголовка mach-o (так что ничего не нужно делать с таблицей символов).

Попытка strip дает странный результат:

$ strip ./a.out
$ gobjdump -T ./a.out

./a.out:     file format mach-o-x86-64

DYNAMIC SYMBOL TABLE:
0000000005614542      d  3c OPT    00 0000 radr://5614542
0000000100000f60 g       0f SECT   01 0080 [.text] __ZN3Foo6getPtrIiEEPcv
0000000100001020 g       0f SECT   08 0080 [.data] __ZZN3Foo6getPtrIiEEPcvE1c
0000000100000000 g       0f SECT   01 0010 [.text] __mh_execute_header
0000000000000000 g       01 UND    00 0200 dyld_stub_binder

Последние две записи не должны быть удалены в любом случае, так как они требуются dyld для обработки этого исполняемого файла. В то же время мы видим, что _main и __Z3barv ушли. Но символы из class Foo все еще там. Единственная разница между ними и раздетыми состоит в том, что у прежних установлен флаг N_WEAK_DEF (0080). Вот немного информации от <mach-o/nlist.h> об этом флаге:

 /*
 * The N_WEAK_DEF bit of the n_desc field indicates to the static and dynamic
 * linkers that the symbol definition is weak, allowing a non-weak symbol to
 * also be used which causes the weak definition to be discared.  Currently this
 * is only supported for symbols in coalesed sections.
 */
#define N_WEAK_DEF  0x0080 /* coalesed symbol is a weak definition */

К сожалению, это не объясняет, почему strip игнорирует символы с этим флагом.

Итак, вопрос в том, как научить strip удалять даже N_WEAK_DEF символов, если пользователь просто не хочет их экспортировать.

P.S. Я изучил параметры команды и не нашел ничего полезного (-N также удаляет неопределенные символы, поэтому это не вариант). Объявление этого класса с visibility("hidden") делает загадку, но, к сожалению, это нелегко сделать с реальным проектом.

Ответы [ 2 ]

0 голосов
/ 31 августа 2018

Благодаря проекту Apple с открытым исходным кодом, я наконец нашел ответ. Похоже, strip никогда не удаляет глобальные символы слабого определения:

    ...
    *
    * In 64-bit applications, we only need to save coalesced
    * symbols that are used as weak definitions.
    */
    ...

(К сожалению, у веб-сайта нет пост-навигационной навигации, или, возможно, я недостаточно тщательно выглядел) Это объясняет мой случай, так как мой двоичный файл mach-o-x86-64.

Тем не менее мотивация такого поведения до сих пор неясна.

РЕДАКТИРОВАТЬ : См. Ответ Кристины, чтобы понять, почему strip предпочитает сохранять глобальные слабые определения.

0 голосов
/ 30 августа 2018

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

Я сделаю некоторые предположения и угадаю из того факта, что кажется, что вы используете перемещения nlist, а не новые перемещения на основе байт-кода (вы можете проверить это, посмотрев команду dyld info load), это либо построен с использованием древней цепочки инструментов или является файлом MH_OBJECT для основного исполняемого файла, который не прошел последний этап компоновки. Я не уверен на 100%, если это так, но в любом случае,

Извините за мое предположение, приведенное выше, но первоначальный ответ по-прежнему применяется, если вы не хотите действительно отказываться от объединения символов, в этом случае создайте свое приложение с частной связью, но опять-таки этот шаблонный экземпляр делает символ слабым по очень веской причине. У него статический конструктор и неявно созданный шаблон, он предпочитает безопасность, поэтому хранит символ. Вы не можете экспортировать его вообще за пределы исполняемого файла, хотя у вас есть небольшой пример, программы на C ++ обычно используют такие вещи, как boost или библиотеки C ++, которые зависят от других библиотек C ++, которые создают цепочки, и в итоге вы получаете несколько определения в общем пространстве имен только из-за семантики C ++. В вашем небольшом тестовом примере вы можете справиться с этим, в более крупном приложении, если вы действительно не знаете, что делаете, и изучаете такие вещи, как деревья зависимостей для dylibs, просто позвольте dyld выполнять свою работу. Я думаю, что мой первоначальный ответ по-прежнему применим к основной части, поскольку объясняет, почему ваш символ помечен как слабый (ODR - это концепция, специфичная для C ++, но он обрабатывается разными статическими компоновщиками):


Для более подробного объяснения - это связано с семантикой C ++, а именно с правилом одного определения (ODR), которое является близким, но не тем же понятием, как неспособность иметь повторяющиеся сильные символы в одном и том же пространство имен (я имею в виду пространство имен ссылок, а не пространство имен C ++, это очень быстро сбивает с толку).

Если вы хотите знать, почему он помечен как слабый, то dyld сможет объединить его во время динамического связывания , поскольку повторное использование этого шаблона приведет к его повторному созданию (вызывая нарушение ODR и в зависимости от в контексте ошибки времени ссылки), поскольку это неявный экземпляр, который может требовать или не требовать объединения (что неизвестно до статического или даже динамического времени ссылки, если, конечно, вы не определите его как скрытое, в этом случае вы должны быть чрезвычайно осторожны поскольку семантика будет сильно различаться в зависимости от таких факторов, как модульная сборка или нет (я имею в виду «модули» LLVM, а не модули TS для C ++).

Если бы он не был слабым, вы бы вызывали нарушение ODR по правилам C ++, определяя его как скрытое для более чем 1 единицы перевода (если вы повторно используете этот шаблон, скажем, в заголовке внутри модуля, вы получите дублированный символ ошибки). Вы можете избежать нарушения ODR, так как оно на самом деле не применяется, но будьте готовы к некоторым неприятным сюрпризам (т. Е. С помощью немодулярных сборок, называемых «каждая единица перевода является модулем»).

Определяя его как слабое, dyld может выбрать правильные определения для каждого конечного связанного объекта, будь то общая библиотека или исполняемый файл (и не забывайте об общем кэше) в время выполнения и связывание / переместите их соответствующим образом в другое плоское пространство имен.

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

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

...