linux - 从仅采用 char* 参数的 x86_64 函数中使用 sys_write?

标签 linux assembly nasm x86-64

我目前正在学习汇编,我需要一些关于这个简单程序的帮助。

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.

我应该对 assembly 部件做哪些更改才能使其正常工作?

最佳答案

你这里有一个主要问题,另外两个主要问题可能对这个特定的调用者不可见:

  • 您忘记将 edx 设置为字符串长度。您正在使用的系统调用是
    sys_write(int fd, const char *buf, size_t len);,所以你传递的 len 是你的调用者在 edx 中留下的任何垃圾>.

如果这不能使 sys_write 返回 -EFAULT 而不打印任何内容,那么可能在它检测到问题之前您正在打印一堆二进制垃圾,如果您以非常大的 len 结束?嗯,write 可能会在写入任何内容之前检查整个范围,所以也许您最终在 edx 中得到了一个中等大小的值,并且只是在字符串之后打印了一些二进制垃圾。不过,您的字符串应该在输出的开头。

在调用前使用strace ./my_program 查找,或者GDB 查看edx。 (对于来自 64 位代码的 int 0x80strace 实际上不会正常工作;它的解码就像您使用的是 native 64 位 syscall ABI)。

  • 您破坏了调用者的 rbx 而没有保存/恢复它,这违反了调用约定(x86-64 System V ABI),其中 rbx 是一个调用-保留寄存器。参见 http://agner.org/optimize/有关调用约定的好文档。这可能没有效果;您的 main 可能不会编译为使用 rbx 的代码,因此您要销毁的 rbx 由 CRT“拥有”启动调用 main 的函数。

    奇怪的是,你保存/恢复了 rbp,即使你根本不使用它。

最重要的是,您是 using the 32-bit int 0x80 ABI from 64-bit code !!这就是为什么我一直说 writelengthedx 而不是 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?显示了一些使用 64 位 syscall 进行 sys_write 的示例代码,但它对长度进行了硬编码。您将需要使用 libc 函数或循环来strlen 您的字符串。 (或者一个缓慢但紧凑的 repe scasb)。

关于linux - 从仅采用 char* 参数的 x86_64 函数中使用 sys_write?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49728516/

相关文章:

linux - 邮件客户端无法连接到代理后面的邮件服务器

c - 用 C 编写的操作系统电源控制实用程序

assembly - 在 NASM 中声明常量绝对 64 位地址

c - 为什么这个 x86_64 汇编代码不起作用?

python - 用 python 为 arm 交叉编译 gdb 失败

php odbc_connect to mssql 在 linux/ubuntu 命令行而不是浏览器中工作

assembly - 如何处理 x86 ASM 中的未知长度输入?

assembly - bp 寄存器在基于索引的情况下不起作用

c - 了解 GDB 中基本 C 程序中的 asm 指令

c - Linux 外壳代码 "Hello, World!"