LINUX.ORG.RU

Инициализация переменной размером бинарника

 ,


1

2

Требуется чтоб во время или после сборки начальное значение определённой переменной было равно размеру бинарника на момент окончания сборки.
Нужно это для того, чтоб потом можно было дописать в конец бинарника некоторые данные, а потом во время исполнения читать их по смещению равному значению этой переменной.

Я сделал это так:
1. Собираю временный бинарник с переменной exe_size=1.
2. Его размер записываю в exe_size.
3. Собираю искомый бинарник с новым значением exe_size.

Выглядит это так:
main.c

#include <stdlib.h>
#include <stdio.h>

extern size_t exe_size;

int main(int argc, char *argv[argc+1]) {
	printf("this file size is %zu\n", exe_size);
	return EXIT_SUCCESS;
}
Makefile
default: tmp main

tmp:
	@mkdir tmp

main: tmp/main.o tmp/exe_size_fill_.o
	gcc $^ -o $@
	@if [ `stat -c %s $@` -ne `stat -c %s tmp/main` ]; then\
		echo 'error: wrong exe_size'; rm $@; exit 1; fi

tmp/main.o: main.c
	gcc $< -c -o $@

tmp/exe_size_fill_.o: tmp/exe_size_fill_.c
	gcc $< -c -o $@

tmp/exe_size_fill_.c: tmp/main
	echo '#include <stdlib.h>' > $@
	echo "size_t exe_size = `stat -c %s $<`;" >> $@

tmp/main: tmp/main.o tmp/exe_size_blank.o
	gcc $^ -o $@

tmp/exe_size_blank.o: tmp/exe_size_blank.c
	gcc $< -c -o $@

tmp/exe_size_blank.c:
	echo '#include <stdlib.h>' > $@
	echo 'size_t exe_size = 1;' >> $@

clean:
	rm -rf main tmp

Запускаем make, затем ./main.
Программа выдаёт что-то типа: this file size is 7399.

Вроде задача решена, но меня терзают смутные сомнения: может я изобрёл велосипед или пытаюсь удалить гланды через одно место?
Хочу узнать на этот счёт мнения лоровских экспертов.
Существуют ли для поставленной задачи более менее стандартные способы?

Существуют ли для поставленной задачи более менее стандартные способы?

Прочти описание ELF формата, напиши апликуху, которая будет добавлять секцию с необходимыми данными в файл и модифицировать адресс переменной, которая указывает на данные.

AF ★★★
()

Тобi ld-скрипт нужен.

anonymous
()
Ответ на: комментарий от arturpub

ld не умеет модифицировать файл, он умеет только создавать.
Автору похоже надо модифицировать

AF ★★★
()

а Я бы добавил какой-то маркер в конец бинарника, и потом искал сам маркер. Т.к. размер может очень сильно меняться ... тот же strip, например, или еще что ...

joy4eg ★★★★★
()
Ответ на: комментарий от AF

добавлять секцию с необходимыми данными в файл и модифицировать адресс переменной, которая указывает на данные

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

Замысел состоит в том, чтобы открыть бинарник с помощью int prg_fd = open(argv[0], O_RDONLY);, перейти по смещению exe_size lseek(prg_fd, exe_size, SEEK_SET); и прочитать только те данные которые требуется.

LinuxUser ★★★
() автор топика
Последнее исправление: LinuxUser (всего исправлений: 1)

С чего ты решил, что что-то, записанное в конец бинарника, будет расположено в памяти аналогично адресом выше?

Deleted
()
Ответ на: комментарий от Deleted

Этот дело говорит.

А ОПу лучше озвучить задачу, которую он хочет решить, а не свои костыльные потуги.

anonymous
()
Ответ на: комментарий от Deleted

С чего ты решил, что что-то, записанное в конец бинарника, будет расположено в памяти аналогично адресом выше?

Я такое не решал. Я решил, что что-то, записанное в конец бинарника будет расположено в конце бинарника. См. моё сообщение.

LinuxUser ★★★
() автор топика
Ответ на: комментарий от LinuxUser

запиши в отдельный файл и не парь людям мозг
/thread

anonymous
()

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

ОП думает, что бинарник просто отображается весь в память как есть и «начинает исполняться».

Про стек, .bss и кучу ОП не знает.

anonymous
()
Ответ на: комментарий от LinuxUser

Ну ок, делай переменную, которая будет содержать размер бинаря, делай бинарь, считай его размер, записівай размер в переменную, приписівай в хвост бинаря свои данніе

AF ★★★
()
Ответ на: комментарий от anonymous

А ОПу лучше озвучить задачу, которую он хочет решить, а не свои костыльные потуги.

Задача озвучена в первых двух предложениях в начальном сообщении.
Не зря я всё-таки анонимусов занёс в игнор-лист.

LinuxUser ★★★
() автор топика
Ответ на: комментарий от anonymous

Да не думает он так. Костыльности это не отменяет, конечно же.

arturpub ★★
()
Ответ на: комментарий от LinuxUser

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

Добавь сегмент без флага LOAD.

anonymous
()

Думаю надежнее будет дописать в готовый бинарник данные, а потом dword с их размером, чтобы в программе сикнуться в конец, узнать смещение, и оттуда уже читать.

arturpub ★★
()

1. append-ишь данные к бинарнику 2. дописываешь размер этих данных после них 3. открываешь бинарник, делаешь seek на -4/-8 байт от конца файла и узнаёшь размер прицепленных данных

anonymous
()

Требуется чтоб во время или после сборки начальное значение определённой переменной было равно размеру бинарника на момент окончания сборки.
Нужно это для того, чтоб потом можно было дописать в конец бинарника некоторые данные, а потом во время исполнения читать их по смещению равному значению этой переменной.

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

trex6 ★★★★★
()
Ответ на: комментарий от anonymous

4. strip успешно херит все данные

anonymous
()
Ответ на: комментарий от trex6

Лорчую. Надо пользоваться тем, что дает система. На линуксе это /.../share/progname, на винде папка в pfiles, на макосе .app/Contents/Resources. Собственно это задача уровня make install, а не make build.

arturpub ★★
()

Как костыльный вариант могу предложить взять большую переменную (uint64_t, например), записывать туда какое-нибудь такое случайное число, которое в природе произвольно не встречается (типа 0xDA43F763A893A2EF), а потом заменять его на размер бинарника простым поиском. Перед этим проверять, не встречается ли оно более одного раза.

Но это только если способа лучше нет.

proud_anon ★★★★★
()

Да, анонимус выше ещё лучше предложил:

1. append-ишь данные к бинарнику 2. дописываешь размер этих данных после них 3. открываешь бинарник, делаешь seek на -4/-8 байт от конца файла и узнаёшь размер прицепленных данных

proud_anon ★★★★★
()
Ответ на: комментарий от i-rinat

Уж лучше objcopy --add-section. И грузиться не будет вместе с программой, и смещение из файла извлечь можно, и strip переживёт.

anonymous
()
Ответ на: комментарий от anonymous

и смещение из файла извлечь можно

Разве тогда не придётся ELF парсить?

И грузиться не будет вместе с программой

rodata всё равно mmap'ится. Какая разница, сколько там будет, 4 килобайта или мегабайт?

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от anonymous

Предложил и обоссал своё предложение использованием strip.

Ну так не надо использовать strip. Или сначала использовать strip, а потом дописывать данные.

proud_anon ★★★★★
()
Ответ на: комментарий от LinuxUser

А обязательно в конце? Обычно это решают с помощью механизма «ресурсов», которые суются прямо в бинарь и разумеется доступны для чтения из программы, даже без необходимости открывать файл на диске, и вероятно даже не засовывая эти данные сразу в ОЗУ (возможно просто mmap-ится, утверждать не буду)... В Qt например это запросто.

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Кстати, а почему так делают только в венде? Да и там принято ресурсы сложнее крохотной иконки отдельно шипить.

anonymous
()
Ответ на: комментарий от I-Love-Microsoft

да не хочет он ресурсов, а хочет метаданные для екзешников, такие как: переносимая вместе экзешником иконка, как в винде, сведения об авторе, версии ПО, м.б. ЭЦП, и т.п.

anonymous
()
Ответ на: комментарий от trex6

а как ещё? не в начало же файла и не в середину. в винде, впрочем, сделали проще: .exe — это тупо zip архив.

anonymous
()
Ответ на: комментарий от anonymous

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

trex6 ★★★★★
()
Ответ на: комментарий от trex6

ЭЦП например. ты должен подписывать собраный бинарь. так что или ложить в отдельное файло или тупо пристегивать в хвост.

exception13 ★★★★★
()
Ответ на: комментарий от exception13

Либо должна быть спека, либо бесполезно, т.к. в итоге бинарь изменился и не подходит под ЭЦП.

trex6 ★★★★★
()
Ответ на: комментарий от exception13

Вирусы разные есть. Это был один из простейших вариантов - вирус дописывался в начало файла, при запуске считывал код после собственной длины, записывал его во временный файл и запускал. После выполнения удалял.

pi11 ★★★★★
()

Вот тебе ещё костыль.

Создаёшь extern void *-переменную (например; можешь long long или что тебе удобнее) и инициализируешь её не-нулём, чтобы она попала в секцию .data:

extern void *XXX = (void*) 0xdeadbeefcafebabe;

int main(int argc, char *argv[])
{
        return 0;
}

Находишь её виртуальный адрес с помощью objdump -t:

$ objdump -t embed
…
0000000000601038 g     O .data	0000000000000008              XXX
…

Находишь виртуальный адрес и смещение в файле секции .data:

$ objdump -h embed
Sections:
Idx Name          Size      VMA               LMA               File off  Algn
23 .data         00000018  0000000000601028  0000000000601028  00001028  2**3

Значение переменной находится по смещению 0x00001028 + (0x0000000000601038 - 0x0000000000601028) == 0x1038 == 4152. Чек:

$ dd if=embed of=symbol bs=1 skip=4152 count=8
$ hexdump symbol 
0000000 babe cafe beef dead

Можешь патчить.

P.S. Не нашёл не-костыльных способов доставать смещения значений символов в ELF-файле. Если я сильно накостылял не бейте, лучше обоссыте.

SysVinit-hater
()
Ответ на: комментарий от trex6

а как вы получите доступ к статик переменным не запуская бинаря? да ещё и универсальный для разных бинарей?

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.