Подтвердить что ты не робот

Какая разница между equ и db в NASM?

len:  equ  2
len:  db   2

Являются ли они одинаковыми, производя ярлык, который можно использовать вместо 2? Если нет, то в чем преимущество или недостаток каждой декларации? Можно ли их использовать взаимозаменяемо?

4b9b3361

Ответ 1

Первый 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.

Ответ 2

Резюме

NASM 2.10.09 Выход ELF:

  • db не имеет никаких магических эффектов: он просто выводит байты непосредственно в выходной файл объекта.

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

    Если вы находитесь в текстовом разделе, ваши байты будут выполнены.

    Погода, которую вы используете db или dw и т.д., которая не указывает размер символа: поле st_size в записи таблицы символов не влияет.

  • equ делает символ в текущей строке st_shndx == SHN_ABS магическим значением в его записи в таблице символов.

    Вместо вывода байта в текущее местоположение объектного файла он выводит его в поле st_value записи таблицы символов.

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

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

Теория 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 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.

Docs

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.

Ответ 3

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, а также несколько общих ограничений.

; 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.