将 C 代码转换为 x86-64 程序集

Posted

技术标签:

【中文标题】将 C 代码转换为 x86-64 程序集【英文标题】:Converting C code to x86-64 assembly 【发布时间】:2018-03-30 18:39:48 【问题描述】:

我正在尝试将我的 C 代码转换为 x86-64。我的目标是反转一个链表。传入的两个参数是头 ptr 和偏移量,以获取指针字段的地址(即指向列表中下一个节点的指针)。

据我了解,head ptr是通过rdi寄存器传入的,offset是通过rsi寄存器传入的。当它到达“mov rcx,[rbx]”行时,我不断收到分段错误。当它只是“mov rcx, rbx”并且后面的行从“mov [rbx], rdx”更改为“mov rbx, rdx”时,分段错误就消失了。但是,我最终陷入了无限循环,因为它只是一遍又一遍地简单地分配相同的值。

当我跟随我的 C 代码时,x86-64 中的所有逻辑对我来说都是有意义的,所以我真的处于停滞状态。有任何想法吗?这是我第一次使用 x86-64。

.intel_syntax noprefix
.text
.global reverse_asm_64

reverse_asm_64:
push rbx
push r12

mov rax, 0x0
#headptr
mov rbx, rax
#nextptr
mov rcx, rax
#new_headptr
mov rdx, rax
#head
mov rax, [rdi]

#checks if head is null
cmp rax, 0
je null_ret

#move offset into a register
mov r12, rsi
add rax, r12
#add offset to rax to get the next ptr
mov rbx, rax

while_start:

#checks that next ptr isn't null
cmp rbx, 0x0
je while_done

#setting the next ptr
mov rcx, [rbx]

# *headptr = new_headptr
mov [rbx], rdx

#new_headptr = headptr
mov rdx, rbx

#sets headptr to nextptr
mov rbx, rcx

jmp while_start

while_done:
mov rax, rdx
sub rax, rsi

null_ret:
pop r12
pop rbx
ret

【问题讨论】:

这种看似无用的练习有充分的理由吗? 有一些工具对此非常有用:C 编译器。他们中的许多人可以选择发出汇编代码,而那些没有的可以与反汇编程序配对。 很高兴有人同意这是一个无用的练习。我需要完成课堂作业,但过去几天我一直卡住了。 您没有发布您的 C 代码,但第一个整数/指针参数在 rdi 中传递,第二个在 rsi 中传递。什么是“抵消”?进入链表的位置?你的汇编很难阅读;缩进你的说明而不是标签。此外,与指令在同一行的 cmets 使事情更加紧凑。 另外,您不需要保存/恢复 rbx 和 r12。您可以使用 r8、r9、r10 和 r11 作为临时注册,以及 rax/rcx/rdx/rsi/rdi。 【参考方案1】:

我不愿意只发布我在编写此答案时创建的重新编写的代码。那样你不会学到任何东西。

因此,您可能需要先解决以下问题:

1) 鉴于 linux 有大约 7 个寄存器可以用于临时,似乎不需要推送/弹出 rbx 和 r12。使用其他不需要保存的寄存器。

2) 看起来您将 cmets 置于 他们描述的代码之后(#headptr 等)。这不是阅读您的代码的人所期望的。最常见的是将它放在之前的行上,或者(尤其是在汇编程序中)放在同一行上。

3) C 中的常见做法是在使用之前始终将所有变量(尤其是指针)清零。但是,在 asm 中则更少。尤其是当下一条语句要为同一个寄存器分配不同的值时。这在 C 中不是问题,因为编译器的优化器将简单地丢弃冗余的初始化程序。但是汇编器没有优化器,所以这只是浪费空间/周期。只有零的东西必须归零。

4) 清零寄存器时,使用xor eax, eax 而不是mov。它更小/更快。

5) 如果您的代码是用head_ptr = reverse_asm_64(head_ptr, 16) 调用的,您将需要在取消引用之前检查 rdi 是否为空。

6) 在 asm 中,您应该使用 test rdi, rdi 来查看 rdi 是否为零,而不是使用 cmp rdi, 0。它更小/更快。

7)“将偏移量移入寄存器”说什么?偏移量已经在寄存器中:rsi。为什么要在 r12 中制作副本?

8) 第一次“检查下一个 ptr 是否为空”时,您刚刚将偏移量添加到值中。除非您的偏移量为零,否则这不会达到您的预期。另见 #6。

9) “将偏移量添加到 rax 以获取下一个 ptr”仅执行一次(即在循环之外)。列表中的每个指针不是都需要添加这个偏移量吗?

还有更多,但那是 9 项。开始似乎已经足够了。

【讨论】:

以上是关于将 C 代码转换为 x86-64 程序集的主要内容,如果未能解决你的问题,请参考以下文章

将 C 代码转换为 Web 程序集时出错

C ++会首先转换为程序集吗?

将代码转换为 Neon 程序集

将 MIPS 程序集转换为 C

将 scanf 与 x86-64 GAS 程序集一起使用

x86-64 C Calling Convention