有人可以告诉我以下Assembl代码有什么问题。获取分段错误

时间:2017-10-22 20:29:47

标签: c assembly compiler-construction x86

由于指令,在函数中出现分段错误 movq -8(%rbp),%rax,一个在printf之前。我不明白为什么? 注意:这不是gcc生成的程序集,而是由我编写的编译器。汇编代码几乎与gcc生成的代码类似。

.text
.globl  main
.type   main, @function
main:
pushq   %rbp
movq    %rsp, %rbp
subq    $16, %rsp
movl    $2, -4(%rbp)
leaq    -4(%rbp), %rax
movl    %eax, %edi
movb    $0, %al
call    fcvt2
movl    %eax, -4(%rbp)
leaq    .LC0(%rip), %rdi
movl    -4(%rbp), %esi
movb    $0, %al
call    printf
leave
ret

.globl  fcvt2
.type   fcvt2, @function
fcvt2:
pushq   %rbp
movq    %rsp, %rbp
subq    $32, %rsp
movq    %rdi, -8(%rbp)
leaq    .LC1(%rip), %rdi
movq    -8(%rbp), %rax
movl    (%rax), %esi
movb    $0, %al
call    printf
movq    -8(%rbp), %rax
movl    (%rax), %edi
movl    %edi, %eax
leave
ret

.section    .rodata
.LC1:
.string "It should be : %d\f"
.LC0:
.string "%d\n"

而C程序是:

int fcvt2(int *ip) {
    int i;
    printf("It should be : %d\f", *ip); 
    return *ip;
}

void main() {
    int i;
    i = 2;
    i = fcvt2(&i);
    printf("%d\n",i);
    return;
}

故障点处的gdb输出:

rax            0xffffdd4c   4294958412
rbx            0x0  0
rcx            0x7ffffff7   2147483639
rdx            0x7ffff7dd3780   140737351858048
rsi            0x7fffffffdd48   140737488346440
rdi            0xffffdd4c   4294958412
rbp            0x7fffffffdd30   0x7fffffffdd30
rsp            0x7fffffffdd00   0x7fffffffdd00
r8             0x0  0
r9             0x9  9
r10            0x7ffff7dd1b78   140737351850872
r11            0x246    582
r12            0x400430 4195376
r13            0x7fffffffde30   140737488346672
r14            0x0  0
r15            0x0  0
rip            0x40059c 0x40059c <fcvt2+20>
eflags         0x10206  [ PF IF RF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0

1 个答案:

答案 0 :(得分:1)

调用者中的

null将指针arg截断为movl %eax, %edi。你实际上是fcvt2上的段错误,而不是你声称之前的指令。 (回顾GDB技能的时间?)

mov (%rax),%esi. raxleaq -4(%rbp), %rax中正确生成了它,但是后来你的编译器忘记了它是一个指向32位值的64位指针。 (理想情况下,您希望%rax直接进入arg寄存器。)

偏离主题:如果您不需要保留EAX的高位字节,则leaq -4(%rbp), %rdi的效率低于xor %eax, %eax。我认为您正在为x86-64 SysV可变参数函数约定执行此操作,并且您是正确的,只有movb $0, %al需要说明有多少个XMM寄存器args,而不是整个{{1}所以你做对了。但归零eax是将%al归零的最有效方法。当然,对于非可变函数,你根本不需要这样做,但是你的编译器显然仍处于刚刚开始工作的阶段,所以无条件地执行它不是正确性问题。您永远不需要在%eax中传递任何其他内容,并且函数调用始终被假定为clobber al

(也相关:Haswell/Skylake partial registers have false dependencies: al isn't renamed separately from rax anymore