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

通过gcc生成64位共享库时的“未定义的主引用”

  •  0
  • S1mple  · 技术社区  · 2 年前

    我试图通过gcc将下面的代码组装到共享库中。

    .section .text
    
    .global S_0x400607
    .type S_0x400607, @function
    S_0x400607:
    push %rbp
    mov %rsp,%rbp
    sub $0x10,%rsp
    mov %edi,-0x4(%rbp)
    mov -0x4(%rbp),%eax
    mov %eax,%esi
    lea S_0x400724(%rip),%rdi
    mov $0x0,%eax
    callq printf
    nop
    leaveq
    retq
    
    
    .section .rodata
    
    S_0x400724:
    .byte 0x25
    .byte 0x64
    .byte 0x0a
    .byte 0x00
    

    我在终端中使用了下面的命令,但出现了错误。

    $ gcc zzz_out.s -shared -fPIC  -o libzzz.so
    /usr/bin/ld: /tmp/ccq8FvIT.o: relocation R_X86_64_PC32 against symbol `printf@@GLIBC_2.2.5' can not be used when making a shared object; recompile with -fPIC
    /usr/bin/ld: final link failed: Bad value
    collect2: error: ld returned 1 exit status
    

    然后我在谷歌上搜索了这个问题,并在网上找到了一个看似可行的答案 undefined-reference-to-main-for-shared-library .然后我添加了选项 -no-pie ,使用了下面的命令,但出现了另一个错误。

    $ gcc zzz_out.s -shared -fPIC -no-pie  -o libzzz.so
    /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o: In function `_start':
    (.text+0x20): undefined reference to `main'
    collect2: error: ld returned 1 exit status
    

    看来选择的顺序很重要。但我用了32位的代码。

    .section .text
    
    .globl S_0x8048506
    .type  S_0x8048506, @function
    S_0x8048506:
    push %ebp
    mov %esp,%ebp
    push %ebx
    sub $0x4,%esp
    call S_0x80485CC
    add $_GLOBAL_OFFSET_TABLE_,%eax
    sub $0x8,%esp
    pushl 0x8(%ebp)
    lea S_0x8048670,%edx
    push %edx
    mov %eax,%ebx
    call printf
    add $0x10,%esp
    nop
    mov -0x4(%ebp),%ebx
    leave
    ret
    
    S_0x80485CC:
    mov (%esp),%eax
    ret
    
    .section .rodata
    S_0x8048670:
    .byte 0x25
    .byte 0x64
    .byte 0x0a
    .byte 0x00
    

    gcc工作完美且可靠 libzzz.so 在当前目录中生成。 抱歉,我问了一个简单的问题。我在这个地区是全新的。

    1 回复  |  直到 2 年前
        1
  •  1
  •   Peter Cordes    2 年前

    你只会 undefined reference to `main' 具有 -no-pie .

    我想 -不要馅饼 是压倒一切的 -shared ,所以你告诉GCC链接一个位置相关的 可执行文件 ,根本不是一个共享库。(ELF共享库又名“共享对象”,必须始终与位置无关,不存在非PIC的情况 .so .有趣的事实:PIE可执行文件是ELF共享对象的另一种类型。)


    与链接非饼图可执行文件不同, ld 重写 call printf 通过PLT给你打电话。但是,当链接饼或共享库时,必须手动执行。(更新) ld 也许可以帮你,至少在馅饼里,但这是最近的变化。)

    32位代码 , 打电话给printf 可能与重写 call rel32 运行时要执行的指令 printf 的实际地址,一旦加载到随机基址。但这不适用于64位代码,因为它可能超过+2GiB。类似于为什么 32-bit absolute addresses no longer allowed in x86-64 Linux? 发行版使PIE可执行文件成为默认的GCC配置之后。

    64位代码的选项包括:

    • call *printf@GOTPCREL(%rip) 喜欢 gcc -fno-plt 编译C时会发出。
    • call printf@plt 就像编译器历史上使用的那样。
    • 非派可执行文件不是选项;你在做一个 -分享 图书馆

    有关NASM语法版本以及机器代码的更多详细信息,请参阅 Can't call C standard library function on 64-bit Linux from assembly (yasm) code 下面还有一些例子,包括at&T语法,链接器如何将其“放松”到 打电话给rel32 如果你愿意 call *foobar@GOTPCREL(%rip) 但是 foobar 毕竟是在同一个共享库中定义的(具有“隐藏”可见性,而不是全局的,因此不需要插入符号)。

    看见 Unexpected value of a function pointer local variable 有关PIE与非PIE可执行文件的编译器输出的一些示例(PIE可执行代码也将在共享库中工作。)

    -fno-plt 在32位模式下,代码gen的风格可能不值得,在这种模式下,您没有有效的EIP相对寻址来访问GOT,除非您已经需要一个用于此函数中其他内容的GET指针。