Assembly 在GNU as.intel_语法中区分内存和常量
我有一条用英特尔语法编写的指令(使用gas作为汇编程序),如下所示:Assembly 在GNU as.intel_语法中区分内存和常量,assembly,x86-64,gnu-assembler,intel-syntax,Assembly,X86 64,Gnu Assembler,Intel Syntax,我有一条用英特尔语法编写的指令(使用gas作为汇编程序),如下所示: mov rdx, msg_size ... msg: .ascii "Hello, world!\n" .set msg_size, . - msg 但是,正如我所期望的那样,mov指令正在被组装成mov0xe,%rdx,而不是mov$0xe,%rdx。我应该如何编写第一条指令(或msg_size的定义)以获得预期的行为?使用mov-edx,OFFSET-symbol获取符号“address”作为立即数,而不是从中
mov rdx, msg_size
...
msg: .ascii "Hello, world!\n"
.set msg_size, . - msg
但是,正如我所期望的那样,mov指令正在被组装成
mov0xe,%rdx
,而不是mov$0xe,%rdx
。我应该如何编写第一条指令(或msg_size
的定义)以获得预期的行为?使用mov-edx,OFFSET-symbol
获取符号“address”作为立即数,而不是从中加载作为地址。这适用于实际标签地址以及使用.set
设置为整数的符号
对于64位代码中的msg
地址(不是msg\u size
assemble time常量),您可能需要leardx,[RIP+msg]
用于静态地址不适合32位的饼图可执行文件
在GAS
.intel\u语法noprefix
模式下:
的工作原理类似于AT&T偏移符号
。这有点像MASM$symbol
的工作原理与AT&Tsymbol
(即解引用)的未知符号类似symbol
- 在GAS和NASM/YASM中,
始终是有效地址,而不是立即地址[symbol]
不会从地址加载,但它仍然使用内存操作数机器编码。()LEA
裸
符号的解释取决于声明的顺序
GAS是一个单程汇编程序(返回并填充
符号值(一旦已知)
当它第一次遇到该行时,它决定了mov rdx,symbol
的操作码和编码。早期的msize=。-msg
或.eq
/.set
将使其选择mov reg,imm32
,但稍后的指令仍不可见
对于尚未定义的符号,默认假设是symbol
是某个节中的地址(就像您使用symbol:
或.set symbol.
这样的标签来定义它)。由于GAS.intel\u syntax
类似于MASM而不是NASM,因此裸符号被视为内存操作数
如果将.set
或msg_length=msg_end-msg
指令放在文件顶部,在引用它的指令之前,它们将组合为mov reg,imm32
mov immediate。(与AT&T语法不同,在AT&T语法中,即使对于像1234
这样的数字文本,也总是需要一个$
作为立即数)
例如:源代码和反汇编与objdump-dS交织在一起:
用gcc-g-cfoo.s
组装,用objdump-drwC-s-Mintel-foo.o
反汇编(用as--version
=GNU汇编程序(GNU-Binutils)2.34)。我们得到这个:
0000000000000000 <l1>:
.intel_syntax noprefix
l1:
mov eax, OFFSET equsym
0: b8 01 00 00 00 mov eax,0x1
mov eax, equsym #### treated as a load
5: 8b 04 25 01 00 00 00 mov eax,DWORD PTR ds:0x1
mov rax, big #### 32-bit sign-extended absolute load address, even though the constant was unsigned positive
c: 48 8b 04 25 aa aa aa aa mov rax,QWORD PTR ds:0xffffffffaaaaaaaa
mov rdi, OFFSET label
14: 48 c7 c7 00 00 00 00 mov rdi,0x0 17: R_X86_64_32S .text+0x1b
000000000000001b <label>:
label:
nop
1b: 90 nop
.equ equsym, . - label # equsym = 1
big = 0xaaaaaaaa
mov eax, OFFSET equsym
1c: b8 01 00 00 00 mov eax,0x1
mov eax, equsym #### treated as an immediate
21: b8 01 00 00 00 mov eax,0x1
mov rax, big #### constant doesn't fit in 32-bit sign extended, assembler can see it when picking encoding so it picks movabs imm64
26: 48 b8 aa aa aa aa 00 00 00 00 movabs rax,0xaaaaaaaa
0000000000000000:
.intel_语法noprefix
l1:
mov eax,偏移量相等
0:b8 01 00 mov eax,0x1
mov eax,Equalsym#######作为负载处理
5:8b 04 25 01 00 mov eax,DWORD PTR ds:0x1
mov rax,big######32位符号扩展绝对加载地址,即使常量为无符号正
c:48 8b 04 25 aa移动rax,QWORD PTR ds:0xffffffffaaaaaaaa
胶印标签
14:48 c7 c7 00 mov rdi,0x0 17:R_X86_64_32S.text+0x1b
00000000000000 1B:
标签:
不
1b:90无
.equ equsym,.-标签#eqsym=1
大=0xaaaaaa
mov eax,偏移量相等
1c:b8 01 00 mov eax,0x1
mov eax,eqsym#########被视为直接
21:b8 01 00 mov eax,0x1
mov-rax,big#####常量不适合32位符号扩展,汇编程序在选择编码时可以看到它,所以它选择movab imm64
26:48 b8 aa aa 00 00 movabs rax,0xAAAAA
使用mov-edx,OFFSET-msg_-size
将任何符号(甚至数字文字)视为立即数总是安全的,无论它是如何定义的。因此,它与AT&T$
完全相同,只是当GAS已经知道符号值只是一个数字,而不是某个部分中的地址时,它是可选的为了保持一致性,最好始终使用OFFSET msg_size
,这样,如果将来有程序员移动代码,那么代码的含义就不会改变,这样数据部分和相关指令就不再是第一位了。(包括那些忘记了这些与大多数汇编程序不同的奇怪细节的未来用户。)
顺便说一句,是的同义词,还有一个用于设置值,该值也是的同义词。set
操作数大小:通常使用32位,除非值需要64位
mov-rdx,偏移符号
将组合到mov-r/m64,符号扩展\u-imm32
。除非它是一个负常量,而不是一个地址,否则在很短的长度内(远小于4GiB)不需要它。您也不希望地址为movabsr64,imm64
;那太没效率了
在GNU/Linux下,在位置相关的可执行文件中写入mov-edx,OFFSET-symbol
是安全的,事实上,您应该始终这样做,或者使用leardx,[rip+symbol]
,除非您正在编写将加载到高2GB虚拟地址空间(例如内核)的代码,否则不要签署扩展32位立即数
有关PIE可执行文件是现代发行版中的默认文件的更多信息,请参见
提示:如果您知道AT&T或NASM语法,或NASM语法,请使用它生成所需的编码,然后使用objdump-Mintel
反汇编,以找到的正确语法。英特尔语法noprefx
但这在这里并没有帮助,因为反汇编只会在对象文件中显示数值文本,如mov-edx,123
,而不是mov-edx,OFFSET-name\u-not\u