Linux退出函数

Linux退出函数

我正在尝试了解Linux系统调用机制。我正在读一本书,书中说退出函数看起来像这样(使用 gdb):

mov $0x0,%ebx
mov $0x1,%eax
80 int $0x80

我知道这是一个退出的系统调用,但在我的 Debian 中它看起来像这样:

jmp    *0x8049698
push   $0x8
jmp    0x80482c0

也许有人可以解释我为什么不一样?当我尝试在 0x80482c0 上执行 disas 时,gdb 打印出:

没有函数包含指定的地址。

谢谢!

答案1

您的代码的exit()调用最终会链接到 C 库 (libc) 函数exit(),该函数实际上可能不会执行int $0x80.

代码对exit()函数的调用实际上被编译 call为程序链接表(PLT)中的指令。运行时动态链接器负责将文件映射/usr/lib/libc.so到内存中。那是C 库。运行时动态链接器还修复 PLT 中的条目,最终调用 from 映射的代码/usr/lib/libc.so

据我所知(我使用的是 Arch linux),你的第二个 3 条指令是 PLT 条目,gdb当我单步进入它时,它会调用“exit@plt”。跳转jmp 0x80482c0到另一个地址,最终跳转到libc.so代码中。

您可以通过相当复杂的练习向自己证明这一点。首先,您已经获得了 PLT 表条目的地址,无论gdb告诉您的是jmp *0x8049698“exit@plt”的地址。在我的 x86 Arch linux 机器上:

(gdb) disassemble 0x8048310,+20
Dump of assembler code from 0x8048310 to 0x8048324:
   0x08048310 <exit@plt+0>:     jmp    *0x80496e8
   0x08048316 <exit@plt+6>:     push   $0x10
   0x0804831b <exit@plt+11>:    jmp    0x80482e0

然后做readelf -e _program_ > elf.headers。查看文件elf.headers。您会发现一行文本,上面写着“节标题:”在节标题中的某个位置,您会看到类似这样的内容:

  [ 9] .rel.dyn          REL             08048290 000290 000008 08   A  5   0  4
  [10] .rel.plt          REL             08048298 000298 000020 08  AI  5  12  4
  [11] .init             PROGBITS        080482b8 0002b8 000023 00  AX  0   0  4
  [12] .plt              PROGBITS        080482e0 0002e0 000050 04  AX  0   0 16

“exit@plt”位于地址 0x8048310。就在“.rel.plt”部分。 “.rel.plt”可能代表“重定位程序链接表”。

int $0x80现在我们到达了可能不存在的部分。做ldd _program_。同样,Arch linux x86 是这样说的:

linux-gate.so.1 (0xb77d9000)
libc.so.6 => /usr/lib/libc.so.6 (0xb7603000)
/lib/ld-linux.so.2 (0xb77da000)

看到那个“linux-gate.so.1”了吗?它包含执行系统调用的实际代码。它可能是int $0x80,或者可能是sysenter指令,或者可能是其他东西。 Linux 内核应该将一个“小型共享库”与实际代码一起放入进程的地址空间中,然后将该小型共享库的地址移交给 ELF“辅助向量”。做man vdso一些细节。动态链接器/lib/ld-linux.so.2知道 ELF 辅助向量的详细信息,并最终将地址linx-gate.so.1放入 PLT 中的某个位置,因此实际的 C 函数调用最终可以进行高效的系统调用。

如果您多次调用ldd _program_,您将看到 的地址linux-gate.so.1在每次调用时都不相同。实际上,内核不会每次都将堆栈顶部放在同一地址,以试图迷惑需要知道堆栈位置以执行自己的代码的恶意软件。

相关内容