рука г cc линкер и .word - PullRequest
0 голосов
/ 26 января 2020

Я пытаюсь понять мой файл запуска и файл компоновщика. Так как я действительно не знаю сборку и ее причуды (честно знаю некоторые основы), я застрял как простое .rerective .word

//inside of .data section
. = ALIGN(4);
_sdata = .;

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

В любом случае, вопрос в том, что именно делает этот .word. я знаю, что это относится к _sdata, потому что позже в моем файле запуска он используется как ldr r1, =_sdata. в основном я хочу узнать подробнее, что .word _sdata делает

1 Ответ

1 голос
/ 26 января 2020

Вы путаете язык ассемблера с указанием цепочки инструментов c скрипт компоновщика

.word просто означает поместить значение в программу в этом месте.

Это не инструкция, это директива, но она является частью языка ассемблера для этого ассемблера. Язык ассемблера определяется ассемблером, конкретный инструмент c не является ни архитектурой целевого процессора, ни каким-либо специальным c. Существует много языков ассемблера x86, и AT & T против Intel не является фактором, определяющим их количество. ARM, MIPS и др. c также имеют много разных, обычно несовместимых, ассемблерных языков. Большинство, если это в синтаксисе директивы и метках, комментариях и других подобных вещах. Иногда инструкции.

.globl _start
_start:
    ldr r0,next_add
    bx r0
next:
    bx lr

.word 1,2,3
next_add:
    .word next
.word 0x12345678

собирать, связывать и разбирать

Disassembly of section .text:

08000000 <_start>:
 8000000:   e59f0010    ldr r0, [pc, #16]   ; 8000018 <next_add>
 8000004:   e12fff10    bx  r0

08000008 <next>:
 8000008:   e12fff1e    bx  lr
 800000c:   00000001    andeq   r0, r0, r1
 8000010:   00000002    andeq   r0, r0, r2
 8000014:   00000003    andeq   r0, r0, r3

08000018 <next_add>:
 8000018:   08000008    stmdaeq r0, {r3}
 800001c:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

Я использовал дизассемблер, чтобы посмотреть, что произошло, поэтому игнорируйте разборку, начиная со строки 800000 c, для тех, кто это данные, которые нам нужны, 32-битное число, это элементы, которые мы просили разместить в программе с помощью директивы .word.

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

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

so.s

.text
.globl _start
_start:
    bx lr

.data
.word _tdata
.word _pdata
.word _sdata

so.ld

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .data :
    {
        _tdata = .;
        *(.data*)
        _pdata = .;
        . = ALIGN(8);
        _sdata = .;
    } > ram
}

собрать, связать, разобрать

Disassembly of section .text:

08000000 <_start>:
 8000000:   e12fff1e    bx  lr

Disassembly of section .data:

20000000 <_tdata>:
20000000:   20000000    andcs   r0, r0, r0
20000004:   2000000c    andcs   r0, r0, r12
20000008:   20000010    andcs   r0, r0, r0, lsl r0

на этот раз я использовал .word в разделе .data, а не .text. он может go где угодно, это один из немногих способов размещения битов информации в программе, где вы хотите эти биты.

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

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

Вы можете видеть, что я разместил количество их там.

.data :
{
    _tdata = .;
    *(.data*)
    _pdata = .;
    . = ALIGN(8);
    _sdata = .;

_tdata должен быть установлен на адрес начала .data, который я определил здесь как 0x20000000 (первый элемент в скрипте, использующий адресное пространство оперативной памяти), но как метка на языке ассемблера, это это просто значение, которое не выделяет место для этого элемента, внутренне в инструменте у него есть таблица с именем и значением, подобно метке, значение которой может быть запрошено в коде.

начиная с 0x20000000 мы хотим, чтобы элементы .data были размещены, и поэтому три запрошенных мною слова будут go в 0x20000000 0x20000004 и 0x20000008.

Первый элемент данных, который я запросил, это метка / адрес _tdata мы знаем, что это начало .data или 0x20000000. _pdata - это адрес после размещения элементов .data, поэтому 0x2000000 c будет этим адресом. И мы видим, что линкер генерирует это для нас. Поскольку они являются .words и уже выровнены, и инструмент в любом случае выравнивает по границе слова для этой цели, я изменил его на ALIGN (8).

Теперь. слева, и это говорит, что я хочу, чтобы вы сделали текущий адрес равным тому, что справа, так. = ALIGN (8); означает взять текущий адрес и найти следующий адрес, который имеет это выравнивание (128 бит, 8 байт), даже если это текущий адрес. и измените указатель адреса на это значение. Следующая строка после этого присваивает этой метке / переменной значение указателя адреса.

Так что после 0x2000000 c ALIGN (8) заставил адрес измениться на 0x20000010, а затем _sdata =. вызвал _sdata переменная / метка равным 0x20000010, и компоновщик видит, что кто-то запросил эту глобальную метку / переменную, и он поместил ее для очистки задания связывания.

Для сценариев компоновщика довольно часто иметь такие вещи, которые вы часто будете видеть, когда переменная / метка помещается до и после раздела, чтобы некоторый код мог знать, где начинается этот раздел и насколько велик, например, C программист ожидает, что данные .bss будут обнулены, поэтому один из наиболее распространенных способов - bootstrap обнулить эту память, но чтобы узнать, где находится код, запрашивает компоновщик, создавая эти переменные, а затем, используя их в программе, иногда вы увидите _bss_size_ = _bss_end_ - _bss_start_; в сценарии компоновщика с двумя другими до и после .bss. вы увидите, что .ALIGNs используются так, что код, который обнуляет память, может делать предположения о выравнивании и делать более простую / быструю процедуру заполнения (нет, вы не используете memset (), которая не имеет смысла, вы не можете использовать C, пока вы не bootstrap it и вы не можете bootstrap, используя C функцию, которую нельзя использовать, пока вы не bootstrap C.)

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

Первоначально вам не нужен скрипт компоновщика, вы можете

arm-none-gnueabi-as so.s -o so.o
arm-none-gnueabi-ld -Ttext=0x1000 -Tdata=0x2000 so.o -o so.elf
arm-none-gnueabi-objdump -D so.elf

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...