Понимание того, как $ работает в сборке [дубликат]

Да, вы должны объявить в «конструкторе», если хотите, чтобы список стал свойством объекта, а не свойством класса.

17
задан paxdiablo 22 July 2015 в 04:50
поделиться

3 ответа

Первый из них equate, аналогичный C:

#define len 2

тем, что он фактически не выделяет какое-либо пространство в конечном коде, он просто устанавливает символ len равным 2. Затем, когда вы используете len позже в своем исходном коде, это то же самое, что если вы используете константу 2.

Вторая - define byte, аналогичная C :

int len = 2;

Он действительно фактически выделяет пробел, один байт в памяти, сохраняет там 2 и устанавливает len как адрес этого байта.

Вот какой-то код psuedo-ассемблера, который показывает различие:

line   addr   code       label   instruction
----   ----   --------   -----   -----------
   1   0000                      org    1234
   2   1234              elen    equ    2
   3   1234   02         dlen    db     2
   4   1235   44 02 00           mov    ax     elen
   5   1238   44 34 12           mov    ax     dlen

Строка 1 просто устанавливает адрес сборки как 1234, чтобы сделать это

В строке 2 код не генерируется, ассемблер просто загружает elen в таблицу символов со значением 2. Поскольку код не сгенерирован, адрес не изменяется.

Затем, когда вы используете его в строке 4, он загружает это значение в регистр.

Строка 3 показывает, что db отличается, он фактически выделяет некоторое пространство (один байт) и сохраняет значение в этом пространстве. Затем он загружает dlen в таблицу символов, но придает значение этого адреса 1234, а не постоянное значение 2.

Когда вы позже используете dlen в строке 5, вы получаете адрес, который вы должны были бы разыменовать, чтобы получить фактическое значение 2.

28
ответ дан paxdiablo 24 August 2018 в 22:05
поделиться

Сводка

NASM 2.10.09 Выход ELF:

  • db не имеет никаких магических эффектов: он просто выводит байты непосредственно в выходной файл объекта. Если эти байты находятся перед символом, символ будет указывать на это значение при запуске программы. Если вы находитесь в текстовом разделе, ваши байты будут выполнены. Погода, которую вы используете db или dw и т. Д., Которая не указывает размер символа: поле st_size в записи таблицы символов не влияет.
  • equ делает символ в текущей строке есть волшебное значение st_shndx == SHN_ABS в его записи в таблице символов. Вместо вывода байта в текущее местоположение объекта объекта он выводит его в поле st_value записи в таблице символов.

Все остальное следует из этого.

Чтобы понять, что это на самом деле означает, вы должны сначала понять основы стандарта ELF и relocation .

Теория SHN_ABS

SHN_ABS сообщает компоновщику, что:

  • перемещение не должно выполняться на этом символе
  • поле st_value записи символа должно использоваться как значение непосредственно

Контрастируйте это с «регулярными» символами, в которых значение символа является адресом памяти вместо этого, и поэтому необходимо переместить.

Поскольку это не указывает на память, символы SHN_ABS могут быть эффективно удалены из исполняемого файла компоновщиком, вставляя их.

Но они все еще являются регулярными символами в объектных файлах, а делают там, где есть глобальная память.

Пример использования

section .data
    x: equ 1
    y: db 2
section .text
global _start
_start:
    mov al, x
    ; al == 1
    mov al, [y]
    ; al == 2

Обратите внимание, что поскольку символ x содержит буквальное значение, для него не должно быть никакого разыменования [], как для y.

Если мы хотели использовать x из программы C нам понадобится что-то вроде:

extern char x;
printf("%d", &x);

и установить на asm:

global x

Эмпирическое наблюдение сгенерированного выхода

Мы можем наблюдать, что мы говорили ранее:

nasm -felf32 -o equ.o equ.asm
ld -melf_i386 -o equ equ.o

Теперь:

readelf -s equ.o

содержит:

Num:    Value  Size Type    Bind   Vis      Ndx Name
  4: 00000001     0 NOTYPE  LOCAL  DEFAULT  ABS x
  5: 00000000     0 NOTYPE  LOCAL  DEFAULT    1 y

Ndx is st_shndx, поэтому мы видим, что x есть SHN_ABS, а y - нет.

Также см., что Size есть 0 для y: db никоим образом не сказано y, что он был шириной в один байт. Мы могли бы просто добавить две директивы db для размещения там 2 байтов.

И затем:

objdump -dr equ

дает:

08048080 <_start>:
 8048080:       b0 01                   mov    $0x1,%al
 8048082:       a0 88 90 04 08          mov    0x8049088,%al

Итак, мы видим что 0x1 был введен в инструкцию, а y получил значение адреса переадресации 0x8049088.

Проверено на Ubuntu 14.04 AMD64.

Документы

http://www.nasm.us/doc/nasmdoc3.html#section-3.2.4 :

EQU определяет символ для заданного значения постоянной : когда используется EQU, строка источника должна содержать метку. Действие EQU состоит в том, чтобы определить данное имя метки для значения его (только) операнда. Это определение является абсолютным и не может измениться позже. Так, например,

message         db      'hello, world' 
msglen          equ     $-message

определяет msglen как константу 12. msglen не может затем переопределяться позже. Это также не определение препроцессора: значение msglen оценивается один раз, используя значение $ (см. Раздел 3.5 для объяснения $) в точке определения, вместо того, чтобы оцениваться везде, где оно ссылается, и используя значение $ в точке отсчета.

См. также

Аналогичный вопрос для GAS: Разница между .equ и .word в ARM Assembly? .equiv, похоже, закрывает эквивалент GAS.

1
ответ дан Community 24 August 2018 в 22:05
поделиться

equ: время препроцессора. аналогично #define, но большинству ассемблеров не хватает #undef и не может иметь ничего, кроме атомной константы с фиксированным числом байтов с правой стороны, поэтому поплавки, удваивает, списки не поддерживаются большинством директив equals для ассемблеров.

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

Вот демон nasm, демонстрирующий db:

; I am a 16 byte object at offset 0.
    db '----------------'

; I am a 14 byte object at offset 16
; the label foo makes the assembler remember the current 'tell' of the 
; binary being written.
foo:
    db 'Hello, World!', 0

; I am a 2 byte filler at offset 30 to help readability in hex editor.
    db ' .'

; I am a 4 byte object at offset 16 that the offset of foo, which is 16(0x10).
    dd foo

. Equ может определять только константу вплоть до наибольшего, что поддерживает ассемблер

пример equ, а также несколько общих ограничений.

; OK
ZERO equ 0

; OK(some assemblers won't recognize \r and will need to look up the ascii table to get the value of it).
CR equ 0xD
; OK(some assemblers won't recognize \n and will need to look up the ascii table to get the value of it).
LF equ 0xA

; error: bar.asm:2: warning: numeric constant 102919291299129192919293122 -
; does not fit in 64 bits
; LARGE_INTEGER equ 102919291299129192919293122

; bar.asm:5: error: expression syntax error
; assemblers often don't support float constants, despite fitting in
; reasonable number of bytes. This is one of the many things
; we take for granted in C, ability to precompile floats at compile time
; without the need to create your own assembly preprocessor/assembler.
; PI equ 3.1415926 

; bar.asm:14: error: bad syntax for EQU
; assemblers often don't support list constants, this is something C
; does support using define, allowing you to define a macro that
; can be passed as a single argument to a function that takes multiple.
; eg
; #define RED 0xff, 0x00, 0x00, 0x00
; glVertex4f(RED);
; #undef RED
;RED equ 0xff, 0x00, 0x00, 0x00

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

1
ответ дан Dmitry 24 August 2018 в 22:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: