len: equ 2
len: db 2
Являются ли они одинаковыми, производя ярлык, который можно использовать вместо 2
? Если нет, то в чем преимущество или недостаток каждой декларации? Можно ли их использовать взаимозаменяемо?
len: equ 2
len: db 2
Являются ли они одинаковыми, производя ярлык, который можно использовать вместо 2
? Если нет, то в чем преимущество или недостаток каждой декларации? Можно ли их использовать взаимозаменяемо?
Первый 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
.
Резюме
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.
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.