Как работает $ в NASM?
message db "Enter a digit ", 0xA,0xD
Length equ $- message
Используется ли для получения длины строки?
Как это работает внутри?
2 ответа
$
является адресом текущей позиции перед выдачей байтов (если есть) для строки, в которой она появляется. Раздел 3.5 этого руководства не содержит подробностей.
$ - msg
это как делать here - msg
т. е. расстояние в байтах между текущей позицией (в конце строки) и началом строки. ( См. Также это руководство по меткам и директивам NASM, таким как resb
)
(Связанный: большинство других сборщиков x86 также используют $
так же, за исключением ГАЗА, который использует .
(Период). MMIX ассемблер использует @
, который имеет правильное смысловое значение).
Чтобы понять это лучше, это может помочь увидеть, что происходит, если вы ошибаетесь: в NASM метки рядом друг с другом в памяти вызывают проблемы с печатью. Этот человек использовал
HELLO_MSG db 'Hello, World!',0
GOODBYE_MSG db 'Goodbye!',0
hlen equ $ - HELLO_MSG
glen equ $ - GOODBYE_MSG
в результате чего hlen
включая длину обеих строк.
EQU
оценивает правую часть сразу, до постоянного значения. (В некоторых ассемблерах, таких как FASM, equ
это замена текста, и вы должны использовать glen = $ - GOODBYE_MSG
оценивать с $
на этой позиции, вместо оценки $
в более позднем mov ecx, glen
инструкция или что-то. Но NASM equ
оценивает на месте; использование %define
для замены текста)
С помощью $
в точности эквивалентно помещению метки в начале строки и использованию ее вместо $
,
Пример размера объекта также можно сделать с помощью обычных меток:
msg: db "Enter a digit "
msgend:
Length equ msgend - msg
Length2 equ $ - msg ; Length2 = Length
newline: db 0xA,0xD
Length3 equ $ - msg ; Length3 includes the \n\r LF CR sequence as well.
; sometimes that *is* what you want
Вы можете положить Length equ msgend - msg
где угодно или mov ecx, msgend - msg
непосредственно. (Иногда полезно иметь ярлык на конце чего-либо, например cmp rsi, msgend
/ jb .loop
в нижней части петли.
Кстати, обычно это CR LF, а не LF CR.
Менее очевидные примеры:
times 4 dd $
Собирается так же, как это (но без создания записи таблицы символов или столкновения с существующим именем):
here: times 4 dd here
В times 4 dd $
, $
не обновляет свой собственный адрес для каждого dword, это все еще адрес начала строки. (Попробуйте сами в файле и зашифруйте плоский двоичный файл: все нули.)
Но %rep
блок раскрывается раньше $
, так
%rep 4
dd $
%endrep
действительно производит 0, 4, 8, 12 (начиная с выходной позиции 0
в плоском бинарном для этого примера.)
$ nasm -o foo rep.asm && hd foo
00000000 00 00 00 00 04 00 00 00 08 00 00 00 0c 00 00 00
Кодирование смещений прыжка вручную:
Нормальный прямой call
является E8 rel32
, со смещением, рассчитанным относительно конца инструкции. (т.е. относительно EIP/RIP во время выполнения инструкции, потому что RIP содержит адрес следующей инструкции. Режимы адресации относительно RIP тоже работают таким же образом.) Длина слова составляет 4 байта, поэтому в dd
псевдоинструкция с одним операндом, адрес конца $+4
, Конечно, вы можете просто поставить метку на следующей строке и использовать ее.
earlyfunc: ; before the call
call func ; let NASM calculate the offset
db 0xE8
dd func - ($ + 4) ; or do it ourselves
db 0xE8
dd earlyfunc - ($ + 4) ; and it still works for negative offsets
...
func: ; after the call
вывод разборки (от objdump -drwC -Mintel
):
0000000000400080 <earlyfunc>:
400080: e8 34 00 00 00 call 4000b9 <func> # encoded by NASM
400085: e8 2f 00 00 00 call 4000b9 <func> # encoded manually
40008a: e8 f1 ff ff ff call 400080 <earlyfunc> # and backwards works too.
Если вы получите неправильное смещение, objdump поместит символическую часть как func+8
, например. Относительное смещение в первых 2 инструкциях вызова отличается на 5, потому что call rel32
имеет длину 5 байтов, и они имеют одно и то же фактическое назначение, а не одинаковое относительное смещение. Обратите внимание, что дизассемблер заботится о добавлении rel32 к адресу инструкций вызова, чтобы показать вам абсолютные адреса назначения.
Ты можешь использовать db target - ($+1)
закодировать смещение для короткого jmp
или же jcc
, (Но будьте осторожны: db 0xEB, target - ($+1)
не правильно, потому что конец инструкции на самом деле $+2
когда вы ставите код операции и смещение как несколько аргументов для одного и того же db
псевдо-инструкции.)
Связанные с: $$
это начало текущего раздела, поэтому $ - $$
насколько далеко вы находитесь в текущем разделе. Но это только в текущем файле, поэтому связывание двух файлов, которые помещают вещи в .rodata
отличается от двух section .rodata
блоки в том же исходном файле. Посмотрите, что на самом деле означает значение $$ в спазме.
Безусловно, наиболее распространенное использование times 510-($-$$) db 0
/ dw 0xAA55
дополнить db 0
) загрузочный сектор размером до 510 байт, а затем добавьте подпись загрузочного сектора, чтобы получить 512 байт. ( Руководство NASM объясняет, как это работает)
документация НАСМ
http://www.nasm.us/doc/nasmdoc3.html#section-3.5
NASM поддерживает два специальных маркера в выражениях, позволяющих использовать в вычислениях текущую позицию сборки: маркеры $ и $$. $ оценивается как позиция сборки в начале строки, содержащей выражение; так что вы можете закодировать бесконечный цикл, используя JMP $.
http://www.nasm.us/doc/nasmdoc3.html#section-3.2.4
EQU определяет символ для заданного постоянного значения: при использовании EQU исходная строка должна содержать метку. Действие EQU состоит в том, чтобы определить данное имя метки значением ее (единственного) операнда. Это определение является абсолютным и не может измениться позже. Так, например,
message db 'hello, world' msglen equ $-message
определяет msglen как константу 12