【问题标题】:Segfault calling c function from assembly从程序集中调用 c 函数的段错误
【发布时间】:2018-02-05 21:30:36
【问题描述】:

我正在尝试在汇编程序中设置一些指针(在 x86_64 linux 上运行的 AT&T 语法),然后将它们传递给 C 程序以实质上添加它们的值。当然,这不是实现最终结果的最有效方式,但我正在尝试了解如何使这样的事情发挥作用,以便进一步构建它。 C 程序如下所示:

#include <stdio.h>

extern void iplus(long *a, long *b, long *c){
  printf("Starting\n");
  long r= *a + *b;
  printf("R setup\n");
  *c=r;
  printf("Done\n");
}

这需要三个长指针,将前两个的值相加,然后将该值存储在第三个中。如上所示,它会在每个点打印一条有关其状态的消息,以跟踪发生分段错误的位置。

引用该函数的汇编程序如下:

.extern exit
.extern malloc
.data
vars: .zero 24 /*stores pointer addresses*/
.text
FORI: .ascii "%d\0" /*format for printing integer*/
.global main
main:
and $~0xf, %rsp /*16-byte align the stack*/
movq $8,%rdi
call malloc
movq %rax,(vars+0) /*allocate 8 bytes and put its address into the variable*/
movq $8,%rdi
call malloc
movq %rax,(vars+8)
movq $8,%rdi
call malloc
movq %rax,(vars+16)
movq $3,((vars+0)) /*first addend 3*/
movq $7,((vars+8)) /*second addend 7*/
movq $0,((vars+16))
movq (vars),%rdi
movq (vars+8),%rsi
movq (vars+16),%rdx
call iplus /*call the function with these values*/
movq $FORI,%rdi
movq ((vars+16)),%rsi
call printf /*print the sum, "10" expected*/
call exit

在执行上述程序后,我得到以下输出:

Starting
Segmentation fault (core dumped)

意味着该函数似乎已成功调用,但该函数中的第一个操作long r = *a + *b; 或更早的一些仅在此时成为问题的东西导致了段错误。我期望发生的是,对于 24 字节 vars 保存的三个 8 字节值,malloc 返回的地址(每次分配 8 个字节)被存储。然后,该地址指向一个 8 字节整数,分别设置为 3、7 和 0。这些整数的地址(即 vars 中保存的值)被传递给 iplus 以便对它们求和,然后使用printf 打印总和。由于我无法确定的原因,这反而会导致段错误。

为什么会出现段错误?是否可以使用 C 函数调用来执行这种加法,并且基本上仍然使用双指针的结构?

【问题讨论】:

  • 您没有传递地址。您缺少 $ 标志,例如movq (vars),%rdi 应该是 movq $vars,%rdi 等等。 leaq vars, %rdi 也可以。
  • 您可能已经知道了,但我仍然存在误解,但请确保您看到了其中数据的布局方式。 vars 指的是包含 3 个连续的 8 字节 地址 的数据,这些地址指向由 malloc 分配的内存,这是存储整数的地方。例如(vars+8) 是加数之一的地址,而不是加数本身的值。
  • 哦,所以你希望这个movq $3,((vars+0)) 是双重间接的?不是,x86 中没有这样的东西。那么你需要做movq vars, %rax; movq $3, (%rax)等等。

标签: c assembly segmentation-fault x86-64 function-calls


【解决方案1】:

您不能直接使用驻留在内存中的指针,您应该先将其加载到寄存器中。您输入的双括号将被忽略,因此:

movq $3,((vars+0)) /*first addend 3*/
movq $7,((vars+8)) /*second addend 7*/
movq $0,((vars+16))

和这个是一样的:

movq $3,(vars+0) /*first addend 3*/
movq $7,(vars+8) /*second addend 7*/
movq $0,(vars+16)

相反,您需要(对于每个值):

movq (vars+0), %rax
movq $3, (%rax)

【讨论】:

  • 当您使用符号而不是寄存器时,即使单个括号也是多余的:movq $3, vars 也是相同静态地址的存储。 (但你真的应该使用mov $7, vars+8(%rip),因为 RIP 相对寻址模式比 32 位绝对寻址模式更短且与位置无关。)GAS 没有自动使用 RIP 相对寻址模式的 NASM 的default rel用于裸符号。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-31
  • 1970-01-01
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多