使用 AT&T 语法从另一个共享库调用 libc 函数?

Calling libc functions from another shared library in AT&T syntax?

我正在尝试 assemble 通过 gcc 将下面的代码添加到共享库。

.section .text

.global S_0x400607
.type S_0x400607, @function
S_0x400607:
push %rbp
mov %rsp,%rbp
sub [=10=]x10,%rsp
mov %edi,-0x4(%rbp)
mov -0x4(%rbp),%eax
mov %eax,%esi
lea S_0x400724(%rip),%rdi
mov [=10=]x0,%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

然后我用谷歌搜索了这个问题,在 中得到了一个看似可行的答案。然后我添加了选项 -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 [=13=]x4,%esp
call S_0x80485CC
add $_GLOBAL_OFFSET_TABLE_,%eax
sub [=13=]x8,%esp
pushl 0x8(%ebp)
lea S_0x8048670,%edx
push %edx
mov %eax,%ebx
call printf
add [=13=]x10,%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 在当前目录中生成。 对不起,如果我问了一个简单的问题。我是这个地区的新手。

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

我认为 -no-pie 覆盖了 -shared,所以你告诉 GCC link 一个 position-dependent 可执行文件 ,根本不是 shared-library。 (ELF 共享库又名“共享对象”始终必须是 position-independent,没有 non-PIC .so 这样的东西。有趣的事实:PIE 可执行文件是另一种类型的 ELF 共享对象。)


与 linking non-PIE 可执行文件不同,ld 通过 PLT 为您重写 call printf 为调用。但是当 link 使用 PIE 或共享库时,您必须手动执行。 (较新的 ld 可能会为您做到这一点,至少在 PIE 中是这样,但这是最近的更改。)

在 32 位代码中call printf 可能会汇编并 links 使用运行时修复程序在运行时重写 call rel32 指令成为 printf 在 libc 中的实际地址,一旦它被加载到一个随机的基地址。但这不适用于 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 就像编译器历史上使用的那样。
  • non-PIE 可执行文件不是一个选项;你正在制作一个 -shared 库。

有关此的 NASM-syntax 版本的更多详细信息,以及机器代码,请参阅 。底部还有一些示例,其中包括 AT&T 语法,如果您执行 call *foobar@GOTPCREL(%rip),linker 如何将其“放松”为 call rel32,然后 foobar 定义在毕竟是同一个共享库(具有“隐藏”可见性,不是全局的,因此不需要符号插入)。

有关 PIE 与 non-PIE 可执行文件的编译器输出的一些示例,请参见 (PIE 可执行代码也可以在共享库中运行。)

-fno-plt 风格的 code-gen 在 32 位模式下可能不值得,在这种模式下你没有有效的 EIP-relative 寻址来到达 GOT,除非你已经需要此函数中其他内容的 GOT 指针。