【问题标题】:Confused about 64-bit registers - ASM对 64 位寄存器感到困惑 - ASM
【发布时间】:2020-03-26 18:19:51
【问题描述】:

我目前正在学习汇编,我在 64 位 ubuntu 上使用 Intel 语法,使用 nasm。

所以我找到了两个引用系统调用编号的网站:

这个用于 32 位寄存器(eax、ebx、...):https://syscalls.kernelgrok.com

这一个用于 64 位寄存器(rax、rbx、...):https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64

问题是当我使用 64 位系统调用号时我的代码不起作用,但是当我将 32 位寄存器中的“e”替换为“r”时它可以工作,例如在 sys_write我使用 rbx 来存储 fd 而不是 rdi as 并且它可以工作。

我现在很迷茫。此代码不起作用:

message db 'Hello, World', 10

section .text
global _start
_start: mov rax,4
        mov rdi, 1
        mov rsi, message
        mov rdx, 13
        syscall
        mov rax, 1
        mov rdi, 0
        syscall

【问题讨论】:

  • 如果你写 64 位代码,你应该使用 64 位系统调用。请注意,参数进入与 32 位系统调用不同的寄存器。如果您需要调试帮助,请发布您的代码。
  • 您是否也将代码从使用int 0x80 更改为syscall?
  • 我们不能使用 int 0x80 int 64 bit 吗?
  • section .data message db 'Hello, World', 10 section .text global _start _start: mov rax,4 mov rbx, 1 mov rcx, message mov rdx, 13 int 80h mov rax, 1 mov rdi, 0 int 80h 此代码有效
  • 使用edit 将您声称不起作用的代码放入您的问题中。 int 0x80 系统调用只查看寄存器的低 32 位,这就是为什么您不应该在 64 位代码中使用它们的原因。但这意味着您的代码可以与mov ecx, message 一起正常工作,以此类推,如果 它可以与mov rcx, message 一起工作。见What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?。您是在int 0x80 之前将指针放在esi 或rsi 中吗?当然这样不行,调用约定和电话号码也不同。

标签: linux assembly x86-64 nasm system-calls


【解决方案1】:

运行strace ./my_program - 你做了一个虚假的stat 系统调用,然后write 成功,然后掉到最后并出现段错误。

$ strace ./foo 
execve("./foo", ["./foo"], 0x7ffe6b91aa00 /* 51 vars */) = 0
stat(0x1, 0x401000)                     = -1 EFAULT (Bad address)
write(0, "Hello, World\n", 13Hello, World
)          = 13
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xd} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)

问题不是注册名称,而是电话号码。您使用的是 32 位索书号,但调用的是 64 位 syscall ABI。

呼叫号码和呼叫约定都不同。

int 0x80 系统调用只查看寄存器的低 32 位,这就是为什么您不应该在 64 位代码中使用它们。

您在评论中使用mov rcx, message 发布的代码可以与mov ecx, message 一起正常工作,以此类推,如果它可以与mov rcx, message 一起工作。见What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?。

请注意,写入 32 位寄存器零扩展至完整的 64 位寄存器,因此您应始终使用 mov edi, 1 而不是 mov rdi, 1。 (尽管 NASM 会为您进行此优化以节省代码大小;它们是如此等效,以至于某些汇编程序会默默地为您完成。)

【讨论】:

  • 谢谢伙计,我对所有 32/64 位的东西感到非常困惑,你真的帮了我大忙!另外我认为这是因为我正在使用 mac AND ubuntu 和 Mac OS 64 系统调用号似乎与 linux 32 相同
  • @Ajvar 每个系统上的系统调用号都不同。由于历史原因,一些系统调用号在许多系统上是相同的,但您不应该依赖它。进行系统调用的最佳方式是使用 libc 包装器,因为它们是可移植的,允许您的程序在多个系统上运行。
  • 对于零扩展,我不应该使用 8 位寄存器吗?
  • @Ajvar 否,因为写入 8 位寄存器不会自动零扩展至整个 64 位。仅当您写入 32 位寄存器时才会发生这种情况。
  • @Ajvar: Why do x86-64 instructions on 32-bit registers zero the upper part of the full 64-bit register? 不幸的是,没有 x86 扩展为 mov r/m32, sign_extended_imm8 添加操作码,这将在每个 mov-immediate 上节省 2 个字节以进行注册,或者对于 64 位 regs 的负值节省更多.每条指令 3 个字节,带有内存目标。 AMD64 可以,因为它释放了几个操作码,但 AMD64 非常保守,也许是因为 AMD 不确定它是否会流行:(
猜你喜欢
  • 1970-01-01
  • 2014-06-25
  • 1970-01-01
  • 2021-06-10
  • 1970-01-01
  • 1970-01-01
  • 2011-10-02
  • 2019-09-13
  • 2012-07-22
相关资源
最近更新 更多