Здравствуйте, есть такая задача. Изменить размер сегмента данных в объектном файле. То есть в файле созданным без линковки gcc -c test.c. Сразу скажу честно, задание учебное. Я понимаю принцип расположения всех сегментов,как их посмотреть, как они буду отображены в памяти ( но тут сейчас не все так однозначно, так как используется ASLR). Знаю системные вызовы brk / sbrk , но они же исполняютс в рантайме из программы. Как изменить размер объектного файла + чтобы не повредить программу. Еще такой вопрос, почему в объектнике сегмент данных (именно .data, а не .rodata) записаны нули, если он этот сегмент вообще присутствует. Хотя инициализированные переменные присутсвуют в сорцах. Подскажите пожалуйста, куда смотреть, чтобы сделать задание и объясните на счет вопросов. Спасибо заранее.
> почему в объектнике сегмент данных (именно .data, а не
> .rodata) записаны нули, если он этот сегмент вообще присутствует.
> Хотя инициализированные переменные присутсвуют в сорцах.Возьмем вот такой код для примера, чтобы было о чем говорить:
#include <stdio.h>
#include <stdlib.h>static int x = 0xa, y = 0xb, z = 0xc;
int main(void)
{
int a = 0x1, b = 0x2, c = 0x3;
int r = 0x0;scanf("%d\n", &r);
r += a + b - c;
r *= x + y - z;
r -= z;printf("result: %d\n", r);
return EXIT_SUCCESS;
}Компилируем в объектник таким образом (именно таким):
$ gcc -c -Wall -O0 main.cСмотрим, что в секции .data:
$ objdump -D -j .data main.oВидим следующее:
0000000000000000 <x>:
0: 0a 00
0000000000000004 <y>:
4: 0b 00
0000000000000008 <z>:
8: 0c 00Делаем вывод, что всё работает как надо. Очевидно у вас нет глобальных данных, которые могли бы быть помещены в секцию ".data"? В моем случае в секцию ".data" попадают все глобальные инициализированные данные:
static int x = 0xa, y = 0xb, z = 0xc;
> Изменить размер сегмента данных в объектном файле. То
> есть в файле созданным без линковки gcc -c test.c.Далее текст для Debian-based дистрибутивов (Debian, Ubuntu, Mint).
Утилиты для работы с системными вещами находятся в пакете "binutils". Можно посмотреть вот так:
$ dpkg -L binutils | grep /usr/bin/Список наиболее часто используемых утилит (по крайней мере те, которые я часто использую):
/usr/bin/size
/usr/bin/addr2line
/usr/bin/objcopy
/usr/bin/as
/usr/bin/readelf
/usr/bin/nm
/usr/bin/ar
/usr/bin/strings
/usr/bin/objdump
/usr/bin/elfedit
/usr/bin/strip
/usr/bin/ldУже по названию можно понять, что вам нужна "objcopy". Смотрим ман:
$ man objcopyДалее ищем по слову "section" в мане. Жмем / и ищем по такому паттерну:
^ *--.*sectionДальше ваша творческая работа :)
Проверить размер ".data":
$ size main.o | awk '{print $2}' | tail -1P.S. Если не получится -- пишите, будем думать дальше. Я такого раньше не делал (ибо бессмысленная задач), так что не уверен на 100% что именно objcopy надо юзать.
P.P.S. Если справитесь -- не забудьте поделиться тут решением.
>[оверквотинг удален]
>
Спасибо большое за ответ. Вот пробовал много способов которые нашел в мане. --change-section-address section{=,+,-}val
Первое что попробовал это
--adjust-section-vma section{=,+,-}val
Насколько я понял этот ключ просто меняет адрес на указанную величину, то есть сам размер остается тот же, но адрес меняется. Пробовал различные комбинации, просто назначает другие адреса.
Второе что нашел это
--gap-fill val --pad-to val
Этот ключ заполняет указанными значениями от определенной секции со смещением, до указанного смещения в pad-to val. Проверил работает , увеличивает размер сегмента, заполняя его FF.
Но как уменьшить размер определенного сегменат.
Еще нашел что можно выделит заранее место для хипа или стека, но для данных нельзя, но это и логично вообще.
Подскажите пожалуйста, что можно еще попробовать
> Подскажите пожалуйста, куда смотреть,Мануал на ld или какой вы там линкер изучаете.
> чтобы сделать задание
$ echo'SECTIONS {.data : { *(.data) } += 0x12345678}' > script.ld;
$ ld -T script.ld тут.o куча.о объектников.о системных.о и.o -lcСкрипт не тестил, мож придётся ALIGN делать, и добавлять дыру такого же размера после себя, ... короча сам. :)
> и объясните на ...Это уже платно.
во, даже тут нашел http://www.opennet.me/docs/RUS/gnu_ld/gnuld-3.html
> Изменить размер сегмента данных в объектном файле.Я знаю правильный ответ: Купите оперативки, жмоты!!! :D
>> Изменить размер сегмента данных в объектном файле.
> Я знаю правильный ответ: Купите оперативки, жмоты!!! :DСпасибо Вам большое за ответ. Но дело в том что надо сам объектный файл изменить так чтобы уменьшился размер сегмента данных. А на сколько я понимаю линковщик создаст уже исполняемый файлы.
Подскажите , а как с помощью этого ключа правильно определить смещение сегмента данных чтобы задать в pad-to
--gap-fill val --pad-to val
> Подскажите , а как с помощью этого ключа правильно определить смещение сегмента
> данных чтобы задать в pad-to
>
> --gap-fill val --pad-to val
>Это ваще не в тему.
> Это ваще не в тему.Подтверждаю. Как я и говорил, я не уверен на 100% что в objdump будет нужная функция, просто помнил, что с помощью её выдирал секции из объектников, вот и сказал посмотреть в ту сторону.
> Подскажите , а как с помощью этого ключа правильно определить смещение сегмента
> данных чтобы задать в pad-to
>
> --gap-fill val --pad-to val
>Вкратце посмотрел -- кажется действительно через objdump не выйдет изменить размер секции. Через ld, как предложил pavlinux, можно попробовать, но на выходе линкер дает бинарь, а не объектник, а вам надо объектник, как я понимаю (в качестве примера скрипта линкера можно юзать например /usr/lib/ldscripts/elf_x86_64.x).
Есть другая идея -- дизасемблировать (через objdump) объектник в ассемблерный код и там изменить размер секции data, после чего скомпилировать асм в объектник. Проблема только в том, как дезасемблировать в такой код, который поймет gcc.
Вот пример:
1. Компилируем main.c в асм (main.s):
$ gcc -S main.c -o main.S2. Меняем в main.S размеры переменных (например) в секции .data:
.data
.align 64
.type x, @object
.size x, 64
x:
.long 10
.align 64
.type y, @object
.size y, 64
y:
.long 11
.align 64
.type z, @object
.size z, 64
z:
.long 12т.е. align и size были 4 байта, я сделал 64 (первое что в голову пришло).
3. Компилируем асм в объектник:
$ gcc -c main.SПолучаем объектник с измененной секцией ".data".
В этой цепочке я изменял асм полученный из сырца, а не из объектника. Ваша задача понять, как получить из объектника такой асм файл, чтобы он мог быть скомпилирован обратно в объектник. Остальная процедура будет такая же, как у меня.
Нет, похоже никак не выйдет скомпилировать выхлоп objdump. Так что этот способ тоже не подходит. Вообще странное задание, кому может понадобиться изменять размер секции .data? Как вообще можно изменить её размер, разве что новых данных туда добавить, только они же не будут использованы кодом программы, короче бессмысленное задание немного. И вообще, какая тема? Линкер, системные утилиты, работа с асмом? Т.е. что предполагается должно быть использовано для решения задачи?
> чтобы уменьшился размер сегмента данных1. Ну изначально было "Изменить размер сегмента данных в объектном файле",
поэтому извиняйте, выбрал в сторону увеличения, как имеющее какой-то смысл. :D
2. Куда девать данные из .data? (а их можно много куда засунуть, в оверлей например).
3. Насколько уменьшать?
> А на сколько я понимаю линковщик создаст уже исполняемый файлы.Можно и не линковать: ld -T script.ld -r old.o -o new.o
В общем копипасть оригинал задания.
Ну и последнее,я учебному заданию предшествуют лекции в которых все должно быть описано.
Сдаётся мне, что это задание при устройстве на работу.
В общем, я сделал, как pavlinux предложил, всё работает:1. Копируем соответствующий скрипт линкера в каталог с объектником:
$ cp /usr/lib/ldscripts/elf_x86_64.x ./custom.ld2. Редактируем копию так, чтобы изменился размер секции ".data", например я просто выравнивание большое втыкнул (1024 байта):
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
. = ALIGN(1024);
}3. Перепаковуем линкером используя свой скрипт линкера:
$ ld -T custom.ld -r main.o -o main2.o4. Смотрим размер .data в main2.o:
$ size main2.oПервое что в голову пришло. Ну а дальше редактируем скрипт линкера в зависимости от того, что нужно получить.
>[оверквотинг удален]
> 3. Перепаковуем линкером используя свой скрипт линкера:
>
> $ ld -T custom.ld -r main.o -o main2.o
>
> 4. Смотрим размер .data в main2.o:
>
> $ size main2.o
>
> Первое что в голову пришло. Ну а дальше редактируем скрипт линкера в
> зависимости от того, что нужно получить.Спасибо Вам огромное !! Все получилось , я был близок к Вашему решению. Только не смог найти ключа чтобы не собирать выполняемый файл (искать зависимости). Спасибо огромное еще раз.
> Спасибо Вам огромное !! Все получилось , я был близок к Вашему
> решению. Только не смог найти ключа чтобы не собирать выполняемый файл
> (искать зависимости). Спасибо огромное еще раз.Не за что. Я тоже не знал такой опции, pavlinux подсказал.
$ cat data.c
char data[1024*1024]={0};
int main(){return 0;}
$ gcc -fno-zero-initialized-in-bss -c data.c
$ objdump -h data.o | grep -A1 \\.data
1 .data 00100000 0000000000000000 0000000000000000 00000080 2**6
CONTENTS, ALLOC, LOAD, DATA