代码之家  ›  专栏  ›  技术社区  ›  qwr

gcc x86-32堆栈对齐和调用printf

  •  -1
  • qwr  · 技术社区  · 6 年前

    gcc with -m32 doesn't require this for main

    我有以下测试代码:

    .data
    intfmt:         .string "int: %d\n"
    testint:        .int    20
    
    .text
    .globl main
    
    main:
        mov     %esp, %ebp
        push    testint
        push    $intfmt
        call    printf
        mov     %ebp, %esp
        ret
    

    构建 as --32 test.S -o test.o && gcc -m32 test.o -o test . 我知道存在syscall write,但据我所知,它不能像printf那样打印int和float。

    0000053d <main>:
     53d:   89 e5                   mov    %esp,%ebp
     53f:   ff 35 1d 20 00 00       pushl  0x201d
     545:   68 14 20 00 00          push   $0x2014
     54a:   e8 fc ff ff ff          call   54b <main+0xe>
     54f:   89 ec                   mov    %ebp,%esp
     551:   c3                      ret    
     552:   66 90                   xchg   %ax,%ax
     554:   66 90                   xchg   %ax,%ax
     556:   66 90                   xchg   %ax,%ax
     558:   66 90                   xchg   %ax,%ax
     55a:   66 90                   xchg   %ax,%ax
     55c:   66 90                   xchg   %ax,%ax
     55e:   66 90                   xchg   %ax,%ax
    

    我对生成的推送指令非常困惑。

    1. 如果推送两个4字节的值,如何实现对齐?
    2. 是什么 call 54b 甚至达到?的输出 hd 比赛 objdump

    B+>│0x5655553d <main>                       mov    %esp,%ebp                      │
       │0x5655553f <main+2>                     pushl  0x5655701d                     │
       │0x56555545 <main+8>                     push   $0x56557014                    │
       │0x5655554a <main+13>                    call   0xf7e222d0 <printf>            │
       │0x5655554f <main+18>                    mov    %ebp,%esp                      │
       │0x56555551 <main+20>                    ret  
    

    当一个二进制文件被实际执行时会发生什么,这方面的资源是值得赞赏的,因为我不知道实际发生了什么,而且我读过的教程也没有涵盖它。我正在通读 How programs get run: ELF binaries .

    1 回复  |  直到 6 年前
        1
  •  3
  •   Peter Cordes    6 年前

    i386系统V ABI call

    功能是 允许 例如 scanf scanf Segmentation faults when called from a function that doesn't change RSP

    函数可以依赖于堆栈对齐来获得性能(对齐 double 或数组 以避免在访问缓存线时发生缓存线拆分)。

    通常是函数依赖堆栈对齐的唯一情况 正确性 在编译时使用SSE/SSE2,因此它可以使用16字节对齐所需的加载/存储来复制结构或数组( movaps movdqa ),或实际自动矢量化本地数组上的循环。

    memcpy 使用运行时调度),所以它们仍然可以在奔腾II这样的古老CPU上工作。x86-64系统上的多拱形库应该采用SSE2,但是对于4字节指针,32位函数不太可能有16字节的结构要复制。

    printf 在32位glibc构建中,实际上并不依赖于16字节堆栈对齐的正确性,因此即使在堆栈未对齐时也不会出错。


    0x14 (十进制20)是该位置的内存值。它将在运行时加载,因为您使用 push r/m32 ,不是 push $20 (或一个时间常数,如 .equ testint, 20 testint = 20 ).

    你曾经 gcc -m32 ,因为这是Ubuntu的gcc的默认值。

    0x2014 相对于文件开头的偏移量。如果在运行程序后在运行时反汇编,您将看到一个真实的地址。

    同样适用于 call 54b . 它假定是对PLT的调用(PLT靠近文件/文本段的开头,因此是低地址)。

    如果你用 objdump -drwC ,您将看到符号重新定位信息。)我喜欢 -Mintel

    gcc -m32 -no-pie 做经典姿势- 依赖的 可执行文件。特别是对于32位代码,尤其是在编译C时,我绝对推荐使用 gcc -m32 -no-pie -fno-pie 获取非饼图代码生成以及链接到非饼图可执行文件。(见 32-bit absolute addresses no longer allowed in x86-64 Linux? 更多关于馅饼的信息。)