Для чего используются директивы секции ассемблера? - PullRequest
2 голосов
/ 11 марта 2019

Я пытаюсь изучить сборку ARM.

После написания этой маленькой программы Hello World:

                .global _start

                .text
_start:         ldr     R1,=msgtxt      
                mov     R2,#13          
                mov     R0,#1           
                mov     R7,#4           
                svc     0               

                mov     R7,#1           
                svc     0               


                .data
msgtxt:         .ascii  "Hello World!\n"

                .end

Я заметил, что могу удалить директиву .text и .data, программа будет работать так же хорошо.

Поэтому мне любопытно: все, что я прочитал, подчеркивало тот факт, что раздел .text должен использоваться для кода, а .data для данных. Но здесь, на моих глазах, они, похоже, ничего не делают!

Следовательно, если они не используются для хранения кода и данных соответственно, каково их истинное назначение?

Ответы [ 2 ]

5 голосов
/ 11 марта 2019

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

.text обычно выделяется в разделе только для чтения, наиболее подходящем для кодаожидается, что это не изменится.

.data - это обычно доступный для записи раздел памяти.Я полагаю, что довольно часто вы помещаете вашу строку в .text прямо рядом с данными кода, если не ожидается, что она изменится (или, возможно, архитектура имеет аналогичный сегмент только для чтения).Я бы сказал, что секция .data даже избегает большую часть времени.Зачем?Поскольку секцию .data необходимо инициализировать - скопировать из двоичного файла программы в память при запуске программы.Большинство данных, на которые ссылается ваша программа, могут быть доступны только для чтения, и любая память, которая им нужна для операций, обычно просто выделяется с сегментом .bss, который выделяет часть неинициализированной памяти.

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

Все это очень специфично для целей вашей программы.Например, Game Boy Advance имел «медленную» область памяти 256 КБ, «быструю» область памяти 32 КБ, а затем область «ПЗУ» только для чтения (данные игрового картриджа), которая может составлять несколько мегабайт, и ассемблеры использовали ихразделы памяти:

.data or .iwram  -> Internal RAM (32KB)
.bss             -> Internal RAM uninitialized
.ewram           -> External RAM (256KB)
.sbss            -> External RAM uninitialized
.text or .rodata -> Read only ROM (cartridge size)

В качестве другого примера, SPC-700 (звуковой чип SNES) имел 64 КБ читаемой и записываемой памяти, которая использовалась для всего, но первые 256 байтов имели более быстрый доступ («нулевая страница»).В этом теоретическом случае .data и .text будут назначены на одну и ту же область памяти, то есть они не будут размещены на нулевой странице, и они оба будут использовать одну и ту же память.Там будет пользовательский сегмент для нулевой страницы, и разница между .text и .data будет очень небольшой - просто способ отличить, какие символы в собранной программе указывают на «данные», а какие символы указывают на программукод.

2 голосов
/ 12 марта 2019

GAS (как и большинство ассемблеров) по умолчанию используется в секции .text, и ваши данные только для чтения все еще работают в .text

Все только байты


Вы можете сделать echo 'mov r1, #2' > foo.s и собрать + связать это в двоичный файл ARM (например, с
gcc -nostdlib -static foo.s).Вы можете пошагово выполнить эту инструкцию в GDB.

(Без системного вызова sys_exit ваша программа после этого рухнет, но, конечно, вы можете сделать это и без директив.)

Компоновщик предупредит, что он не нашел символ _start (потому что вы пропустили сам ярлык, не говоря уже о директиве .globl, которая указала ассемблеру сделать его видимым в таблице символов объектного файла.

Но по умолчанию GNU binutils ld использует начало раздела .text в качестве точки входа ELF.

Большинство разделов, кроме .text, не связаны в исполняемую памятьпо умолчанию, поэтому наличие _start: в .data обычно является проблемой.


Данные только для чтения обычно должны помещаться в раздел .rodata, который связан как часть сегмента TEXT.В любом случае, что касается поведения во время выполнения, размещение его в конце .text секции (пропуская .data) в значительной степени точно соответствует тому, что вы должны были сделать.

В чем разница между разделом и сегментом в формате файла ELF

Помещение в .data приводит к тому, что компоновщик помещает его в другой сегмент, который указывает загрузчику программы ELF операционной системы на картуон читает + записывает (и не выполняет).

Смысл в том, чтобы раздел .rodata был отделен от .text, заключался в группировании кода и данных вместе. Многие ЦП разделили L1dи кеши L1i и / или отдельные TLB для данных / инструкций, поэтому детальное смешивание данных только для чтения с пустым пространством кода в разделенных кешах.

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

...