【问题标题】:Segmentation fault ASM on linux at printflinux 上 printf 的分段错误 ASM
【发布时间】:2014-01-24 23:43:00
【问题描述】:

以下是一本书(Introduction to 64 Bit Intel Assembly Language Programming for Linux,Seyfarth,2012 年)第 9 章中的程序。错误(在 gdb 中)是:

程序收到信号SIGSEGV,分段错误。 0x00007ffff7aa10a5 in __printf_size (fp=0x400400, info=0x0, args=) 在 printf_size.c:199 199 printf_size.c: 没有这样的文件或目录。

直到本章,我成功地使用了以下内容来“生成一个目标文件”,正如推荐的那样,

yasm -f elf64 -g dwarf2 -l exit.lst exit.asm

然后,

ld -o prgm prgm.o

这是从书中复制的程序(l 10 push rbp;我首先rem'd ;但结果相同):

    segment .text
    global  main
    extern  printf

;   void print_max  ( long a, long b )
;   {
a   equ 0
b   equ 8
print_max:
    push    rbp;     ;normal stack frame
    mov     rbp, rsp
;   leave space for a, b and max
    sub     rsp, 32
;   int max;
    max equ 16
    mov [rsp+a], rdi ; save a
    mov [rsp+b], rsi ; save b
;   max = a;
    mov [rsp+max], rdi
;   if ( b > max ) max = b;
    cmp rsi, rdi
    jng skip
    mov [rsp+max], rsi
skip:
    ;   printf ( "max(%1d,%1d ) = %1d\n",
    ;                a, b, max );
    segment .data
fmt db  'max(%1d,%1d) = %1d',0xa,0
    segment .text
    lea rdi, [fmt]
    mov rsi, [rsp+a]
    mov rdx, [rsp+b]
    mov rcx, [rsp+max]
    call printf
 ; }
    leave
    ret

main:
    push    rbp
    mov     rbp, rsp
;   print_max ( 100, 200 );
    mov     rdi, 100    ;first parameter
    mov     rsi, 200    ;second parameter
    call    print_max
    xor     eax, eax    ;to return 0
    leave
    ret

在与本章中的前一个程序(“Hello World”示例)发生类似的分段错误之后,我使用了

gcc -o prgm prgm.o

在这个程序之前一直有效。

【问题讨论】:

  • 那些%1d(一个d)格式说明符应该是%ld(ell d)吗?
  • @mbratch 是的,看起来是这样。 Kindle 非常温和地区分了 ell 和 one,尽管我应该注意到这一点。谢谢你。当格式说明符被替换为long时,在原始程序中,如果没有按照Gunner的建议进行修改,仍然会发生分段错误。

标签: linux assembly 64-bit yasm


【解决方案1】:

如果您要使用 C 库中的函数,使用 gcc 链接是最简单的方法,因为 gcc 会在“幕后”为您处理一些事情。

要仅使用 ld,您需要链接到 ld-linux-x86-64.so.2 并将其传递给 -lc 以链接到 C 库。

接下来,你用错了printf。如果你没有使用浮点寄存器(你不是),你需要将rax“归零”。

此外,由于您正在链接 C 库,因此您不能只从 main 中调用 ret,而是调用 exit

lea     rdi, [fmt]
mov     rsi, [rsp+a]
mov     rdx, [rsp+b]
mov     rcx, [rsp+max]
xor     rax, rax                      ; # of floating point registers used.
call    printf

和:

;   print_max ( 100, 200 );
mov     rdi, 100    ;first parameter
mov     rsi, 200    ;second parameter
call    print_max
xor     eax, eax    ;to return 0
leave

xor     rdi, rdi
call    exit

ld -o $(APP) $(APP).o -lc -I/lib64/ld-linux-x86-64.so.2

和输出:

最大(100,200)= 200

【讨论】:

  • 在 extern 指令中添加 exit 后,根据您的建议,程序没有出错。谢谢。
  • 在 printf 之前将 rax 清零,用 xor eax, eax 代替 xor rax, rax 是否可行?它似乎在程序中工作。
  • @user3043627: ret 来自主要作品,除非你破坏了main 的返回地址或更高的堆栈。不知道为什么会出错,但您只需要从_start 手动退出。您可以从 main 调用 exit,但如果 ret 不起作用,则表明存在错误。
【解决方案2】:

Gunner 给出了精彩的总结。该程序应该在 rax 中放置了一个 0。这可以使用“xor eax, eax”来完成,这是在 x86-64 模式下将寄存器归零的正常方法。寄存器的上半部分用 32 位寄存器的 xor 清零,下半部分取决于使用的 2 个寄存器的位(使用 eax,eax 结果为 0)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-12
    • 1970-01-01
    • 2021-02-23
    • 1970-01-01
    相关资源
    最近更新 更多