LINUX.ORG.RU

Как линкер настраивает символы в таблице символов?

 , , ,


0

3

Приветствую.

У меня имеется следующий скрипт линкера:

ENTRY (_start)

SECTIONS
{
        . = 0x00100000;
	/* The kernel will live at 3GB + 1MB in the virtual address space, */
	/* which will be mapped to 1MB in the physical address space. */
	/* Note that we page-align the sections. */

	_kernel_start = .;
        .multiboot.data : {
            *(.multiboot.data)
        }

       .multiboot.text : {
           *(.multiboot.text)
       }

	. += 0xC0000000;
	/* Add a symbol that indicates the start address of the kernel. */
	.text ALIGN (4K) : AT (ADDR (.text) - 0xC0000000)
	{
		*(.text)
	}
	.rodata ALIGN (4K) : AT (ADDR (.rodata) - 0xC0000000)
	{
		*(.rodata)
	}
	.data ALIGN (4K) : AT (ADDR (.data) - 0xC0000000)
	{
		*(.data)
	}
	.bss ALIGN (4K) : AT (ADDR (.bss) - 0xC0000000)
	{
		*(COMMON)
		*(.bss)
		*(.bootstrap_stack)
	}
	/* Add a symbol that indicates the end address of the kernel. */
	_kernel_end = .;
}

В коде объявляются несколько символов

.section .bss, "aw", @nobits
	.align 4096
boot_page_directory:
	.skip 4096
boot_page_table1:
	.skip 4096

И затем, для доступа к данным, что скрываются за ними, необходимо вручную вычитать 0xC0000000

.section .multiboot.text, "a"
.global _start
.type _start, @function
_start:
	# Physical address of boot_page_table1.
	movl $(boot_page_table1 - 0xC0000000), %edi

Почему так происходит? Ведь используя директиву AT() я указываю фактический адрес в который загрузится секция bss, а значит и данные которые в ней хранятся, следовательно символ boot_page_table1 должен указывать на фактический адрес данных и не требовать вычитания.

Получается, таблица символов формируется лишь исходя из счётчика позиции(который «.»)? Или что-то ещё влияет? Если я объявлю символ, который будет указывать на данные в .multiboot.data,

.section .multiboot.data, "aw"
some_data:
	.skip 1024

то должен ли я при обращении к нему производить вычитание 0xC0000000?

Я использую для линковки gcc



Последнее исправление: mrjbom (всего исправлений: 4)

то должен ли я при обращении к нему производить вычитание 0xC0000000

Возможно я что-то не понимаю, но

/* The kernel will live at 3GB + 1MB in the virtual address space, */
/* which will be mapped to 1MB in the physical address space. */
/* Note that we page-align the sections. */
0xC0000000 = 3 ГБ

Т.е. ядро в ВАП живёт на смещении в 3Гб и нужно учитывать это смещение

AKonia ★★★
()
Последнее исправление: AKonia (всего исправлений: 3)
.text ALIGN (4K) : AT (ADDR (.text) - 0xC0000000)

Т.е. разместить .text там, где .text, но ниже? Может линкер этого не понимает. И зачем делать + 0xC0000000, а потом - 0xC0000000?

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

Да, в ВАП оно живёт там, но фактически(об этом говорит директива AT() линкера) оно загружается выше первого мегабайта(ниже 3 ГБ) ФАП. Посему я и ожидаю, что символы boot_page_table1 и boot_page_directory будут указывать туда, где фактически загружена секция bss(ниже 3 ГБ), однако они указываются на место, где данные окажутся после включения виртуальных адресов.

Стоит отметить, что изначально виртуальные адреса(paging) отключены.

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

Ну вообще, это делается затем, что-бы используя виртуальные адреса, создать иллюзию того, что ядро загружено в верхней половине памяти(выше 3 ГБ). Можно почитать тут если что.

И зачем делать + 0xC0000000, а потом - 0xC0000000?

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

Так а работать это как будет? Линкер может разместить символы только по одному адресу в общем случае. Чем AT() отличается от ., кроме того, что одно явное, а второе нет?

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

Символ в моём случае ссылается на область памяти исходя из счётчика позиции(а может и ещё чего), а не того, что фактически секция на которую ссылается этот символ, будет загружена по другому адресу. Меня интересует почему так происходит. Сам вопрос описан в последнем абзаце вопроса.

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

OK, посмотрел что такое AT(). Это физический адрес, куда оно грузится. По умолчанию он равен виртуальному и о нём можно не думать.

Как я говорил, у символа один адрес, виртуальный (значение счётчика позиции). Загружаться он может куда угодно, но доступен будет по виртуальному и значением символа тоже будет виртуальный адрес. Так что да, вычитать нужно.

Если добавить линкеру -Map=kernel.map, то видно что только «load address» секций меняется от AT():

.text           0x0000000000007e00        0xb load address 0x0000000000000000
xaizek ★★★★★
()

AT

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

. += 0xC0000000;

AT (ADDR (.bss) - 0xC0000000)

Какой смысл прибавлять счётчик, если ты потом всё равно все секции грузишь с вычитанием? Убери прибавление счётчика и AT и всё будет так, как хочешь, без необходимости в коде вычитать 0xC0000000.

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

Либо в коде, до запуска основного кода, копируй из (ADDR (.text) - 0xC0000000) _kernel_end - .text байт в 0xC0000000.

Для .bss, кстати, вообще странно указывать AT, туда же ничего не загружается.

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

Спасибо за наиболее адекватный ответ. А «добавить линкеру -Map=kernel.map» это что? Куда это добавлять? Что за kernel.map? Хотелось бы самому на это глянуть.

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

Это параметр для сохранения таблицы размещения в файл с именем kernel.map. Такой же как -T для скрипта линкера. Если ld напрямую не вызывается, то надо писать -Wl,-Map=kernel.map для gcc.

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

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

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