Как работает $ в 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

Другие вопросы по тегам