[bits 32]
global _start
section .data
str_hello db "HelloWorld", 0xa
str_hello_length db $-str_hello
section .text
_start:
mov ebx, 1 ; stdout file descriptor
mov ecx, str_hello ; pointer to string of characters that will be displayed
mov edx, [str_hello_length] ; count outputs Relative addressing
mov eax, 4 ; sys_write
int 0x80 ; linux kernel system call
mov ebx, 0 ; exit status zero
mov eax, 1 ; sys_exit
int 0x80 ; linux kernel system call
这里的关键是我需要获得hello字符串的长度以便作为参数传递给Linux的sys_write系统调用。现在,我知道可以使用EQU就可以顺利工作,但我真的想了解这里发生了什么。
基本上,当我使用EQU时,它会加载值,这很好。
str_hello_length equ $-str_hello
...
...
mov edx, str_hello_length
然而,如果我在使用DB时使用此行代码。
str_hello_length db $-str_hello
...
...
mov edx, [str_hello_length] ; of course, without the brackets it'll load the address, which I don't want. I want the value stored at that address
与我预期的加载该地址处的值不同,汇编器输出了RIP相对寻址(在gdb调试器中显示),我只是想知道为什么。
mov 0x6000e5(%rip),%edx # 0xa001a5
现在,我尝试使用eax寄存器(然后将eax移动到edx),但是我遇到了不同的问题。在gdb中注意到,我最终会得到一个分段错误:
movabs 0x4b8c289006000e5,%eax
看起来不同的寄存器会产生不同的代码。我猜我需要以某种方式截断高32位,但我不知道怎么做。
虽然我找到了一个“解决方案”,方法如下: 使用 str_hello_length 的地址加载 eax,然后加载 eax 指向的地址的内容,一切都很顺利。
mov eax, str_hello_length
mov edx, [eax] ; count
; gdb disassembly
mov $0x6000e5,%eax
mov (%rax),%edx
显然,试图间接从内存地址加载值会产生不同的代码?我不是很了解。
我只需要帮助理解这些指令的语法和操作,以便更好地理解如何加载有效地址。是的,我想我本可以切换到EQU并继续前进,但我真的觉得在未理解DB声明及其地址加载方式之前我无法继续。
rip
和rax
不存在。因此,问题在于您认为您正在生成32位代码,而实际上您有64位代码。也就是说,在x86_64中,即使您没有在方括号中明确指定rip
,生成rip
相对内存地址仍然是默认设置。 - Gunther Piezgcc -m32
进行链接,或者使用适当的 ld 标志 (-m elf_i386
)。 - mirabilos