这会让汇编器在汇编时为你计算字符串长度
$ 是当前位置的地址之前 发射它出现的行的字节(如果有的话)。 Section 3.5 of the manual 没有详细说明。
$ - msg 类似于here - msg,即当前位置(字符串末尾)和字符串开头之间的字节距离。 (See also this tutorial NASM 标签和指令,如resb)
(相关:大多数其他 x86 汇编器也以相同的方式使用 $,除了 GAS 使用 .(句号)。MMIX assembler 使用 @,具有正确的语义含义)。
为了更好地理解它,看看当你弄错时会发生什么可能会有所帮助:In NASM labels next to each other in memory are printing both strings instead of first one。这个人用过
HELLO_MSG db 'Hello, World!',0 ; normally you don't want ,0
GOODBYE_MSG db 'Goodbye!',0 ; in explicit-length strings, unless it also needs to be a C-string
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 is E8 rel32,相对于指令的end计算位移。 (即在指令执行时相对于 EIP/RIP,因为 RIP 保存下一条指令的地址。RIP 相对寻址模式也以这种方式工作。) dword 是 4 个字节,所以在 dd 伪指令中一个操作数结束的地址是$+4。您当然可以在 next 行上放一个标签并使用它。
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) 是不对的,因为当您将操作码和位移都作为同一 db 伪指令的多个 args 时,指令的结尾实际上是 $+2。)
相关:$$ 是当前部分的开始,所以$ - $$ 是您进入当前部分的距离。但这仅在当前文件中,因此链接两个将内容放入 .rodata 的文件不同于在同一源文件中拥有两个 section .rodata 块。见What's the real meaning of $$ in nasm。
到目前为止,最常见的用法是times 510-($-$$) db 0 / dw 0xAA55 将引导扇区(使用db 0)填充到 510 字节,然后添加引导扇区签名以生成 512 字节。 (The NASM manual explains how this works)