我目前正在学习装配,我需要一些这方面的帮助。
say_something()函数是由x86_64程序集(在64位Linux上使用NASM)创建的,并且应该打印出短语和换行符。
主程序用C语言编写,如下所示:
extern void say_something(char *);
int main(void) {
say_something("First line.");
say_something("Second sentence on another line.");
return 0;
}
... * .asm文件的内容是:
section .text
global say_something
say_something:
push rbp
mov rbp, rsp
mov rax, 4 ; syscall for write
mov rbx, 1
mov rcx, rdi
int 0x80
pop rbp
ret
但显然我的ASM功能有问题,因为当我编译程序并运行它时,输出只是一些垃圾,而它应该打印出来:
First line.
Second sentence on another line.
我应该对装配件进行哪些更改才能使其正常工作?
答案 0 :(得分:0)
这里有一个主要问题,以及这个特定来电者可能看不到的另外两个主要问题:
edx
设置为字符串长度。系统呼叫您正在使用的是
sys_write(int fd, const char *buf, size_t len);
,因此您传递的len
是您的来电者在edx
留下的任何垃圾。如果这不会使sys_write
返回-EFAULT
而不打印任何内容,那么可能在您检测到问题之前打印了一堆二进制垃圾,如果您最终一个非常大的len
?嗯,write
可能在写任何东西之前检查整个范围,所以也许你最终在edx
中得到了一个中等大小的值,并在你的字符串之后打印了一些二进制垃圾。不过,你的字符串应该在输出的开头就可以了。
使用strace ./my_program
查找,或使用GDB在通话前查看edx
。 (strace
实际上无法从64位代码正确地使用int 0x80
;它会像使用本机64位syscall
ABI一样进行解码。
你破坏了调用者的rbx
而没有保存/恢复它,违反了调用约定(x86-64 System V ABI),其中rbx
是一个保持呼叫的寄存器。有关调用约定的详细文档,请参阅http://agner.org/optimize/。这可能没有效果;您的main
可能无法编译为使用rbx
进行任何操作的代码,因此您销毁的rbx
已被"拥有"通过调用main
的CRT启动函数。
奇怪的是,即使您根本不使用它,也可以保存/恢复rbp
。
最重要的是,你using the 32-bit int 0x80
ABI from 64-bit code !!这就是为什么我一直在说write
将其length
置于edx
而不是rdx
,因为32位int 0x80
ABI是与32位用户空间可以使用的ABI完全相同,它仅查看低32位寄存器。
您的代码将破坏虚拟地址空间低32位之外的指针。例如如果使用gcc -fPIE -pie
编译,即使.rodata
部分中的字符串文字也不在低32位,sys_write
可能会返回-EFAULT
而不做任何事情。
-pie
is the default for gcc on many modern Linux distros,所以你很幸运"(?)您的程序根本打印了任何内容,而不仅仅write
失败-EFAULT
。您没有检查错误返回值,并且它不会以您尝试从用户空间中的坏点读取/写入的方式对程序进行分段,因此它无声地失败。
正如@fuz指出的那样,Why cant i sys_write from a register?显示了一些示例代码,用于将syscall
用于sys_write
,但它会对一段长度进行硬编码。您需要使用libc函数或循环来strlen
字符串repe scasb
。 (或者是缓慢而紧凑的java.lang.NullPointerException:
Attempt to read from field 'java.lang.String com.example.juang.trainingplan.Objects.Edit.full_name' on a null object reference at
com.example.juang.trainingplan.Edit_athlete$4.onDataChange(Edit_athlete.java:100)
)。