$ 究竟是如何在 NASM 中工作的?
Posted
技术标签:
【中文标题】$ 究竟是如何在 NASM 中工作的?【英文标题】:How does $ work in NASM, exactly? 【发布时间】:2018-05-09 17:40:02 【问题描述】:message db "Enter a digit ", 0xA,0xD
Length equ $- message
是用来获取字符串长度的吗? 它在内部是如何工作的?
【问题讨论】:
你试过reading the documentation吗? 我试过了,但看不懂!$
通常被称为“位置计数器”。 (在其他汇编器中可以是*
或.
)
相关:如果您将Length equ $- message
放在错误的位置会发生什么可能会帮助您了解它是如何工作的:***.com/questions/26897633/…
(I tried but could not understand!
失败并没有错。但是记录和展示你尝试过的东西并没有什么坏处——即使不言而喻你尝试了某事 在寻求帮助之前。)
【参考方案1】:
这会让汇编器在汇编时为你计算字符串长度
$
是当前位置的地址之前 发射它出现的行的字节(如果有的话)。 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)
【讨论】:
很好的例子!您也许可以添加另一个使用db
、dd
和$
手动创建call rel32
指令的指令。
@fuz:感谢您的建议。终于有时间完成这个编辑了。
有趣的事实:MMIX 汇编器使用@
而不是$
,因为@
清楚地表示了我们所在的位置。
@PeterCordes 我们可以认为 $ 等于 RIP,我正在阅读一些代码,我发现了这个 lea rsi, [rel $ +0xffffffffffffffff ]
,这真的让我很困惑很多关于这应该是什么意思?
@zerocool: 不,RIP 相对寻址是相对于 next 指令的开始,但$
是 this 的开始操作说明。 [rel $ - 1]
将是该 LEA 指令之前的字节,使用 RIP 相对寻址模式寻址。 (我没有检查机器代码以查看 NASM 是否实际组装它)。 [rip - 1]
将是 LEA 指令的最后一个字节,即 rel32 的高字节。以上是关于$ 究竟是如何在 NASM 中工作的?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 iPhone 模拟器中工作的 UIAutomation 中获取 captureScreenWithName?