Наш проект (C ++, Linux, gcc, PowerPC) состоит из нескольких общих библиотек. При выпуске новой версии пакета должны изменить только те библиотеки, чей исходный код был фактически затронут. Я имею в виду «изменение» абсолютная двоичная идентичность (контрольная сумма по файлу сравнивается. Другая контрольная сумма -> другая версия в соответствии с политикой). (Следует отметить, что весь проект всегда создается сразу, независимо от того, был ли изменен какой-либо код или нет для каждой библиотеки).
Обычно это может быть достигнуто путем скрытия закрытых частей включенных файлов заголовков и без изменения общедоступных.
Однако был случай, когда простой delete
был добавлен в деструктор класса TableManager (в файле TableManager.cpp!) библиотеки libTableManager.so, и все же двоичный файл / контрольная сумма библиотеки libB.so (которая использует класс TableManager) имеет изменено.
TableManager.h:
class TableManager
{
public:
TableManager();
~TableManager();
private:
int* myPtr;
}
TableManager.cpp:
TableManager::~TableManager()
{
doSomeCleanup();
delete myPtr; // this delete has been added
}
Проверяя libB.so с readelf --all libB.so
Посмотрев на .дынсым раздел, оказалось, что длина всех функций, даже динамически используемых из других библиотек, хранятся в libB! Это выглядит так (длина 668 в 3-м столбце):
527: 00000000 668 FUNC GLOBAL DEFAULT UND _ZN12TableManagerD1Ev
Итак, мои вопросы:
Бинго. На самом деле это своего рода «ошибка» в binutils, которую они нашли и исправили в 2008 году. Информация о размере на самом деле не полезна!
Что Саймон Болдуин писал в списке рассылки binutils описана именно проблема (выделена мной):
В настоящее время размер неопределенного символа ELF копируется из
объектный файл или DSO, который предоставляет символ, при связывании. Этот размер
ненадежный, например, в случае двух DSO, одна из которых связана с
Другой. DSO нижнего уровня может внести изменения, сохраняющие ABI, которые
изменяет размер символа без каких-либо жестких требований по восстановлению
высокоуровневый DSO. И если DSO верхнего уровня перестраивается, инструменты, которые
контрольные суммы файла монитора будут регистрировать изменения из-за измененного размера
неопределенного символа, хотя ничего больше о
DSO более высокого уровня изменился. Это может привести к ненужному и
нежелательные перестроения и изменения каскадов в системах на основе контрольной суммы.
У нас проблема со старой системой (binutils 2.16). Я сравнил его с версией 2.20 в настольной системе и — вуаля — длины общих глобальных символов были 0:
157: 00000000 0 FUNC GLOBAL DEFAULT UND _ZN12TableManagerD1Ev
158: 00000000 0 FUNC GLOBAL DEFAULT UND _ZNSs6assignERKSs@GLIBCXX_3.4 (2)
159: 00000000 0 FUNC GLOBAL DEFAULT UND sleep@GLIBC_2.0 (6)
160: 00000000 0 FUNC GLOBAL DEFAULT UND _ZN4Gpio11setErrorLEDENS_
Поэтому я сравнил оба исходных кода binutils, и, вуаля, снова есть исправление, предложенное Аланом в списке рассылки:
Может быть, мы просто применим патч и перекомпилируем binutils, так как нам нужно оставаться на более старой платформе. Спасибо тебе за твое терпение.
Вы должны были бы прочитать код для загрузчика, чтобы быть уверенным, но я думаю, что в этом случае мы можем сделать довольно разумное предположение о том, для чего предназначено это поле длины.
Загрузчик должен взять все функции, которые будут включены в процесс, и сопоставить их с адресами памяти. Итак, это дает первой функции адрес. Затем вторая следует после конца первой, но чтобы знать «конец первой», нужно знать, какова длина первой функции.
Я могу увидеть два способа приблизиться к получению этой длины: он может либо закодировать его в файле (как вы видели это в ELF), либо он может открыть файл, содержащий функцию, и получить длину из там.
Последний, как мне кажется, имеет два довольно очевидных недостатка. Первый — это скорость — открывать все эти дополнительные файлы, анализировать их заголовки и т. Д., Просто чтобы получить длины функций, почти наверняка медленнее, чем чтение дополнительных четырех байтов для каждой функции из текущего файла. Второе — удобство: если вы не вызываете ни одну из функций в файле, вам вообще не нужен этот файл. Если вы считываете длины непосредственно из файла (например, как обычно это делает Windows с DLL), вам потребуется, чтобы этот файл присутствовал в целевой системе, даже если он никогда не использовался.
Редактировать: Так как некоторые люди, очевидно, пропустили (очевидно, слишком) скрытое значение «предназначено для достижения», позвольте мне быть совершенно ясным: я уверен, что это поле фактически не используется (и никогда не использовалось).
Любой, кто считает, что этот ответ неверен, должен вернуться к программированию 101 и изучить разницу между интерфейсом и реализацией.
В этом случае формат файла определяет интерфейс — набор возможностей, которые может использовать загрузчик. Похоже, что в конкретном случае Linux это поле никогда не используется.
Это, однако, не меняет того факта, что поле все еще существует, или что ОП спросил о том, почему оно существует. Простое изречение «оно не используется», хотя и само по себе верно, не отвечает / не отвечает на заданный им вопрос.