У нас есть большой набор проектов C ++ (GCC, Linux, в основном статические библиотеки) с множеством зависимостей между ними. Затем мы скомпилируем исполняемый файл с использованием этих библиотек и развернем двоичный файл на внешнем интерфейсе. Было бы чрезвычайно полезно иметь возможность идентифицировать этот двоичный файл. В идеале нам нужен небольшой скрипт, который бы извлекал следующую информацию непосредственно из двоичного файла:
$ident binary
$binary : Product=PRODUCT_NAME;Version=0.0.1;Build=xxx;User=xxx...
$ dependency: Product=PRODUCT_NAME1;Version=0.1.1;Build=xxx;User=xxx...
$ dependency: Product=PRODUCT_NAME2;Version=1.0.1;Build=xxx;User=xxx...
Таким образом, он должен отображать всю информацию для самого двоичного файла и для всех его зависимостей.
В настоящее время наш подход:
Во время компиляции для каждого продукта мы генерируем Manifest.h и Manifest.cpp, а затем внедряем Manifest.o в двоичный файл.
Подлинный скрипт анализирует целевой двоичный файл, находит там сгенерированный материал и печатает эту информацию
Однако этот подход не всегда надежен для разных версий gcc.
Я хотел бы спросить SO сообщества — есть ли лучший подход для решения этой проблемы?
Спасибо за любой совет
Один из уловов с хранением данных в исходном коде (ваш Manifest.h
а также .cpp
) является лимитом размера для литеральных данных, который зависит от компилятора.
Мое предложение заключается в использовании ld
, Это позволяет вам хранить произвольные двоичные данные в вашем файле ELF (как и objcopy
). Если вы предпочитаете написать собственное решение, взгляните на libbfd
.
Допустим, у нас есть hello.cpp
содержащий обычный пример C ++ «Hello world». Теперь у нас есть следующий файл make (GNUmakefile
):
hello: hello.o hello.om
$(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@
%.om: %.manifest
ld -b binary -o $@ $<
%.manifest:
echo "$@" > $@
Здесь я делаю этап связывания, потому что я хочу, чтобы манифест (после преобразования в формат объекта ELF) также был связан с двоичным файлом. Поскольку я использую суффиксные правила, это один путь, другие, безусловно, возможны, включая лучшую схему именования для манифестов, где они также заканчиваются как .o
файлы и GNU make могут понять, как их создать. Здесь я подробно рассказываю о рецепте. Итак, мы имеем .om
файлы, которые являются манифестами (произвольными двоичными данными), созданными из .manifest
файлы. Рецепт утверждает, что преобразовать двоичный вход в объект ELF. Рецепт создания .manifest
Сам по себе просто передает строку в файл.
Очевидно, что сложная часть в вашем случае не хранит данные манифеста, а скорее генерирует их. И, честно говоря, я слишком мало знаю о вашей системе сборки, чтобы даже попытаться предложить рецепт .manifest
поколение.
Все, что вы бросаете в свой .manifest
файл, вероятно, должен представлять собой некоторый структурированный текст, который может интерпретироваться упомянутым вами скриптом или который может даже выводиться самим двоичным файлом, если вы реализуете переключатель командной строки (и игнорируете .so
файлы и .so
файлы взломаны, чтобы вести себя как обычные исполняемые файлы при запуске из оболочки).
Приведенный выше файл make не учитывает зависимости — точнее, он никак не помогает создавать список зависимостей. Вы, вероятно, можете заставить GNU помочь вам в этом, если вы четко выразите свои зависимости для каждой цели (то есть статических библиотек и т. Д.). Но, возможно, не стоит идти по этому пути …
Также посмотрите на:
Если вам нужны конкретные имена для символов, сгенерированных из данных (в вашем случае манифест), вам нужно использовать немного другой маршрут и использовать метод, описанный Джоном Рипли. Вот.
Как получить доступ к символам? Легко. Объявите их как внешние (C linkage!) Данные и затем используйте их:
#include <cstdio>
extern "C" char _binary_hello_manifest_start;
extern "C" char _binary_hello_manifest_end;
int main(int argc, char** argv)
{
const ptrdiff_t len = &_binary_hello_manifest_end - &_binary_hello_manifest_start;
printf("Hello world: %*s\n", (int)len, &_binary_hello_manifest_start);
}
Символы являются точными символами / байтами. Вы также можете объявить их как char[]
, но это приведет к проблемам в будущем. Например. для printf
вызов.
Причина, по которой я сам вычисляю размер, заключается в том, что а.) Я не знаю, гарантированно ли буфер завершен нулем, и б.) Я не нашел никакой документации по взаимодействию с *_size
переменная.
Примечание: *
в строке формата говорит printf
что он должен прочитать длину строки из аргумента, а затем выбрать следующий аргумент в качестве строки для печати.
Вы можете вставить любые данные в .comment
раздел в вашей выходной двоичный файл. Вы Можно сделайте это с компоновщиком после факта, но, вероятно, проще поместить его в ваш код C ++, например:
asm (".section .comment.manifest\n\t"".string \"hello, this is a comment\"\n\t"".section .text");
int main() {
....
asm
заявление должно идти вне любая функция, в этом случае. Это должно работать до тех пор, пока ваш компилятор помещает нормальные функции в .text
раздел. Если это не так, вы должны сделать очевидную замену.
Линкер должен собрать все .comment.manifest
разделы на один BLOB-объект в конечном двоичном файле. Вы можете извлечь их из любого .o
или исполняемый с этим:
objdump -j .comment.manfest -s example.o
Задумывались ли вы об использовании стандартной системы упаковки вашего дистрибутива? В нашей компании тысячи пакетов, и сотни из них автоматически развертываются каждый день.
Мы используем пакеты debian, которые содержат всю необходимую информацию:
Я думаю, что вам не нужно создавать манифесты по-своему, как только готовое решение уже существует. Вы можете взглянуть на Пакет Debian HowTo здесь.