10.2.4 Загрузка файла bFLT |
Предыдущая Содержание Следующая |
Загрузка простого ("flat") файлового формата обрабатывается в функции load_flat_file. Шаги в процедуре загрузки следующие:
1.Сначала функция читает разные поля заголовка и рассчитывает необходимую память и тип требующейся загрузки.
2.Затем загрузчик отображает разделы файла в оперативную память или берёт их из флеш-памяти на основе установленных флагов в заголовке файла. Например, если файл имеет разделы со сжатыми данными или текстом, то загрузчик должен сначала считать такой файл в память и распаковать эти разделы. Распечатка 10.1 показывает каждый логический шаг в этом процессе. 3.Загрузчик настраивает структуру задачи процесса, указывая информацию о местонахождении стека, данных и текста.
4.Наконец, он выполняет модификацию адресов, как показано в Распечатке 10.2.
Проверка на наличие GOT производится с помощью переменной flags, полученной ранее из заголовка файла bFLT. Также отметим, что datapos является началом смещением таблицы GOT после отображения файла в память. Теперь загрузчику необходимо модифицировать каждую запись, присутствующую в GOT. Функция calc_reloc делает необходимое исправление адреса, как показано в следующем фрагменте:
static unsigned long calc_reloc(unsigned long r, struct lib_info *p, int curid, int internalp) { … … if (r < text_len) /* В текстовом сегменте */ addr = r + start_code; else /* В сегменте данных */ addr = r - text_len + start_data;
return addr; }
Расчёт прост. Если адрес, который должен быть модифицирован, находится в пределах текстового сегмента, то к нему добавляется начальный адрес текстового сегмента. В противном случае это адрес в сегменте данных и, следовательно, модификация адреса осуществляется на основе начального адреса сегмента данных. После модификации записей GOT, записи таблицы переадресации проходят и приводятся в порядок через функцию calc_reloc. Чтобы понять тонкости перемещения, рассмотрим пример.
Случай FRB (полностью перемещаемых бинарных файлов)
Создадим файл sample.c с пустой функцией main.
Sample.c
main {}
Компилируем и создаём файл типа "flat". О том, как компилировать файлы FRB с корректными параметрами компилятора, смотрите Раздел 10.7.1. Пусть выходной файл называется sample, а файл символов будет symbol.gdb. Заголовок файла "flat" выведен с использованием программы flthdr.
#flthdr sample
Magic: bFLT Rev: 4 Entry: 0x48 Data Start: 0x220 Data End: 0x280 BSS End: 0x290 Stack Size: 0x1000 Reloc Start: 0x280 Reloc Count: 0x1c Flags: 0x1 ( Load-to-Ram )
Вывод может быть напрямую соотнесён с заголовком bFLT, описанным выше. Поскольку это перемещаемый бинарный файл, обратите внимание на установленный в заголовке флаг Load-to-RAM (загрузка в память). Также Reloc Count равен 0x1C, то есть для перемещения пустого файла main было создано 28 записей. Чтобы разгадать эту тайну, давайте посмотрим на файл символов, symbol.gdb.
# nm sample.gdb 00000004 T _stext 00000008 T _start 00000014 T __exit 0000001a t empty_func 0000001a W atexit 0000001c T main 00000028 T __uClibc_init 0000004a T __uClibc_start_main 000000ba T __uClibc_main 000000d0 T exit … … 00000214 D _errno 00000214 V errno 00000218 D _h_errno 00000218 V h_errno 0000021c d p.3 … … 00000240 B __bss_start 00000240 b initialized.10 00000240 B _sbss 00000240 D _edata 00000250 B _ebss 00000250 B end
Эта распечатка показывает различные символы, находящиеся в файле. Поскольку любое приложение должно быть связано с libc, то в этой распечатке символов все выведенные символы, кроме 0x1c T main(), взяты из libc, в данном случае это uClibc. Данные и текст libc имеют в себе точки для перемещения, указанные в таблице переадресации: 28 записей для перерасчёта. Теперь в файл sample.c добавим код.
Sample.c
int x=0xdeadbeef; int *y=&x;
main () { *y++; }
Снова скомпилируем и распечатаем заголовок файла.
#flthdr sample
Magic: bFLT Rev: 4 Entry: 0x48 Data Start: 0x220 Data End: 0x280 BSS End: 0x290 Stack Size: 0x1000 Reloc Start: 0x280 Reloc Count: 0x1f Flags: 0x1 ( Load-to-Ram )
Обратите внимание на увеличение счётчика записей переадресации с 0x1c до 0x1f; созданы три дополнительные записи переадресации. В новом sample.c в разделе данных создана запись для перерасчёта адреса y, указывающего на x. Также увеличение содержимого y создаёт запись для перемещения в текстовом разделе. Используя nm для данных sample.gdb получаем следующие адреса x и y:
… 00000200 D x 00000204 D y …
В таблицу переадресации добавлена запись для y. Таблица содержит запись 204, которая должна быть перерасчитана. Также должен быть перерасчитан адрес, указываемый в 204 (то есть 200). Это то, что выполняется в Распечатке 10.2. Ниже приводится простая интерпретация кода перерасчёта адресов. Сделаем распечатку sample с помощью od. Мы выводим здесь только таблицу переадресации, начиная с байта со смещением 280, как указано в заголовке этого файла.
#od –Ax –x sample –j280
000280 0000 1e00 0000 2400 0000 2c00 0000 3600 000290 0000 4000 0000 4600 0000 6400 0000 6c00 0002a0 0000 7600 0000 7e00 0000 8c00 0000 9e00 …
Теперь обратим внимание на вторую ненулевую запись 0x2400. Шаги, выполняемые для перерасчёта, следующие:
▪Шаг 1:
▪Шаг 2:
▪Шаг 3:
▪Шаг 4:
Чтобы подвести итог, на Рисунке 10.4 показаны различные этапы выполнения перерасчёта. Для текстовой ссылки также будет создана аналогичная запись.
Рисунок 10.4 Перерасчёт адресов файла типа "flat".
Случай PIC
Снова возьмём файл sample.c с пустой функцией main.
Sample.c
main {}
Скомпилируем программу с включённым параметром PIC и изучим созданный код.
#flthdr sample
Magic: bFLT Rev: 4 Entry: 0x48 Data Start: 0x220 Data End: 0x2e0 BSS End: 0x2f0 Stack Size: 0x1000 Reloc Start: 0x2e0 Reloc Count: 0x2 Flags: 0x2 ( Has-PIC-GOT )
Обратите внимание, что есть только две записи для перерасчёта и поле flags указывает на наличие GOT. Напомним, что GOT присутствует в начале раздела данных с последней записью, указанной с помощью -1. Чтобы изучить содержимое нашего файла и увидеть наличие GOT, мы используем od.
#od –Ax –x sample
000000 4662 544c 0000 0400 0000 4800 0000 2002 000010 0000 e002 0000 f002 0000 0010 0000 e002 000020 0000 0200 0000 0200 1342 678b 0000 0000 … … 000220 0000 0000 0000 0000 0000 0000 0000 6802 000230 0000 7c02 0000 a002 0000 aa01 0000 7402 000240 0000 6002 0000 6c02 0000 7002 0000 2001 000250 0000 8802 0000 6402 0000 2800 0000 1c00 000260 0000 0000 0000 0000 0000 0000 0000 c800 000270 0000 0000 0000 0001 0000 4400 0000 0000 000280 ffff ffff 0000 0000 0000 0000 0000 0000 000290 0000 0000 0000 0000 0000 0000 0000 0000 …
Обратим внимание на начало GOT по адресу 0x220, как указано в Data Start: 0x220, и конец GOT по адресу 0x280, определяемому по значению 0xFFFFFFFF, в общей сложности 16 действительных (ненулевых) записей GOT. Все эти записи GOT предназначены для стандартных символов libc. Теперь в sample.c снова добавим наши строчки, скомпилируем и распечатаем заголовок.
Sample.c
int x=0xdeadbeef; int *y=&x;
main () { *y++; }
#flthdr sample
Magic: bFLT Rev: 4 Entry: 0x48 Data Start: 0x220 Data End: 0x2e0 BSS End: 0x2f0 Stack Size: 0x1000 Reloc Start: 0x2e0 Reloc Count: 0x3 Flags: 0x2 ( Has-PIC-GOT )
Сразу же замечаем увеличение счётчика Reloc на 1. Это перерасчёт для x по адресу в y. Как и ожидалось, ссылка на у в тексте создаёт запись в GOT. С помощью od сделаем распечатку sample.
000220 0000 0000 0000 0000 0000 0000 0000 7002 000230 0000 8402 0000 a002 0000 b601 0000 7c02 000240 0000 6802 0000 7402 0000 7802 0000 2c01 000250 0000 9002 0000 6c02 0000 3400 0000 1c00 000260 0000 6402 0000 0000 0000 0000 0000 0000 000270 0000 d400 0000 0000 0000 0c01 0000 5000 000280 ffff ffff 0000 0000 0000 0000 0000 0000
У нас есть в общей сложности 17 записей в GOT, то есть одна дополнительная запись. Нам необходимо определить ту запись GOT, которая соответствует y. Выполним nm sample.gdb и посмотрим переменные, как и раньше.
… 00000260 D x 00000264 D y …
Выделенная жирным запись выше в таблице GOT показывает запись для y. Загрузчик в Распечатке 10.2 выполняет проверку на флаг GOT и сначала перемещает записи GOT, а затем продолжает модифицировать записи. Рисунок 10.5 показывает, как перерасчитывается запись GOT.
Рисунок 10.5 Перерасчёт записи GOT.
Подводим итог нашего обсуждения в этом разделе.
▪uClinux использует формат файла bFLT (Binary FLAT) . ▪bFLT может быть либо FRB, либо PIC. ▪FRB имеет абсолютные ссылки на данные и текст, записи переадресации указывают на места, которые должны быть исправлены загрузчиком. ▪PIC работает c адресацией относительно текста и, следовательно, требует поддержки на уровне платформы. Ссылки на данные в тексте делаются с помощью GOT. GOT содержит указатели данных, которые исправляются загрузчиком. ▪XIP предусматривает запуск файлов с флеш-памяти, экономя таким образом на текстовом пространстве, которое в противном случае занимало бы место в оперативной памяти
|
Предыдущая Содержание Следующая |