$ NAS如何在NASM中工作?

Nav*_*ash 4 x86 assembly nasm

message db "Enter a digit ", 0xA,0xD
Length equ $- message
Run Code Online (Sandbox Code Playgroud)

它用于获取字符串的长度吗?
它在内部如何运作?

Pet*_*des 13

$是发出它出现的行的字节(如果有)之前的当前位置的地址. 本手册的第3.5节没有详细介绍.

$ - msg就像在做here - msg,即当前位置(在字符串末尾)和字符串开头之间的字节距离.(另请参阅本教程中有关NASM标签和指令的内容resb)

(相关:$除了使用.(句点)的 GAS之外,大多数其他x86汇编程序也使用相同的方式.MMIX汇编程序使用@,具有正确的语义含义).


为了更好地理解它,可能有助于了解当你弄错时会发生什么: 在NASM中,内存中彼此相邻的标签会导致打印问题.这个人用过

HELLO_MSG db 'Hello, World!',0
GOODBYE_MSG db 'Goodbye!',0

hlen equ $ - HELLO_MSG
glen equ $ - GOODBYE_MSG
Run Code Online (Sandbox Code Playgroud)

导致hlen包括两个字符串的长度.

EQU立即评估右侧,达到恒定值.(在一些像FASM这样的汇编程序中,equ是一个文本替换,你必须用它glen = $ - GOODBYE_MSG来评估$这个位置,而不是$在后面的mov ecx, glen指令或其他东西中equ进行评估.但是NASM的评估是在现场; %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
Run Code Online (Sandbox Code Playgroud)

你可以放在Length equ msgend - msg任何地方,或mov ecx, msgend - msg直接.(有时在某些东西的末尾有一个标签很有用,例如cmp rsi, msgend/ jb .loop在循环的底部.

顺便说一句,它通常是CR LF,而不是LF CR.


不太明显的例子:

times 4  dd $
Run Code Online (Sandbox Code Playgroud)

汇编与此相同(但不创建符号表条目或与现有名称冲突):

here:    times 4 dd here
Run Code Online (Sandbox Code Playgroud)

times 4 dd $,$不会更新到每个双字的自己的地址,它仍然是该行的起始地址.(在文件中单独尝试并使用hexdump平面二进制文件:它全部为零.)


但是%rep之前的块已经扩展$,所以

%rep 4
    dd $
%endrep
Run Code Online (Sandbox Code Playgroud)

确实产生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  
Run Code Online (Sandbox Code Playgroud)

手动编码跳跃位移:

正常直接callE8 rel32,相对于指令的结束计算位移.(即,在执行指令时相对于EIP/RIP,因为RIP保存下一条指令的地址.RIP相对寻址模式也是这样工作的.)dword是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
Run Code Online (Sandbox Code Playgroud)

反汇编输出(来自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.
Run Code Online (Sandbox Code Playgroud)

如果你得到错误的偏移,objdump会把符号部分作为func+8例如.前2个调用指令中的相对位移相差5,因为它call rel32是5个字节长并且它们具有相同的实际目的地,而不是相同的相对位移.请注意,反汇编程序负责将rel32添加到调用指令的地址,以显示绝对目标地址.

您可以使用db target - ($+1)短消息编码jmp或偏移jcc.(但要注意:db 0xEB, target - ($+1)不正确,因为指令的结尾实际上$+2是将操作码和位移作为同一db伪指令的多个参数.)


相关:$$是当前的开始部分,所以$ - $$你是如此遥远的当前部分.但这仅在当前文件中,因此链接两个放入内容的文件.rodata不同于section .rodata在同一源文件中有两个块.请参阅nasm中$$的真正含义.

到目前为止,最常见的用途是times 510-($-$$) db 0/ dw 0xAA55垫(有db 0)引导扇区出510个字节,然后添加引导扇区签名,使512个字节.(NASM手册解释了这是如何工作的)

  • 很好的例子!您可以添加另一个,使用`db`,`dd`和`$`手动创建`call rel32`指令. (2认同)
  • 有趣的事实:MMIX汇编器使用`@`而不是`$`,因为`@`清楚地表明了我们所在的位置。 (2认同)
  • @zerocool:不,RIP 相对寻址是相对于*下一个*指令的开始,但“$”是*这个*指令的开始。“[rel $ - 1]”是 LEA 指令之前的字节,使用 RIP 相对寻址模式进行寻址。(我没有检查机器代码来看看 NASM 是否实际上是这样组装的)。“[rip - 1]”将是 LEA 指令的最后一个字节,即 rel32 的高字节。 (2认同)