c - 在 gcc 32 位代码中未定义的对 `_GLOBAL_OFFSET_TABLE_' 的引用,用于一个简单的函数,独立的操作系统

标签 c gcc assembly osdev got

我有一个小的 c 代码文件(function.c):

int function()
{
    return 0x1234abce;
}

我使用的是 64 位机器。但是,我想写一个小的 32 位操作系统。我想将代码编译成“纯”程序集/二进制文件。

我编译我的代码:
gcc function.c -c -m32 -o file.o -ffreestanding # This gives you the object file
我将它与:
ld -o function.bin -m elf_i386 -Ttext 0x0 --oformat binary function.o
我收到以下错误:
function.o: In function `function':
function.c:(.text+0x9): undefined reference to `_GLOBAL_OFFSET_TABLE_'

最佳答案

您需要-fno-pie ; default (in most modern distros) is -fpie : generate code for a position-independent executable .这是一个独立于 -pie 的代码生成选项。链接器选项(默认情况下 gcc 也会通过),并且独立于 -ffreestanding . -fpie -ffreestanding意味着您想要一个使用 GOT 的独立 PIE,这就是 GCC 的目标。
-fpie仅在 64 位代码中花费一点速度(其中 RIP 相对寻址是可能的),但对于 32 位代码来说非常糟糕;编译器在其中一个整数寄存器中获得指向 GOT 的指针(占用 8 个寄存器中的另一个),并使用 [reg + disp32] 访问相对于该地址的静态数据。寻址模式,如 [eax + foo@GOTOFF]
禁用优化,gcc -fpie -m32在寄存器 中生成 GOT 的地址即使该函数不访问任何静态数据。如果您查看编译器输出(在您正在编译的机器上使用 gcc -S 而不是 -c),您会看到这一点。

On Godbolt我们可以使用 -m32 -fpie提供与配置了 --enable-default-pie 的 GCC 相同的效果:

# gcc9.2 -O0 -m32 -fpie
function():
        push    ebp
        mov     ebp, esp                        # frame pointer
        call    __x86.get_pc_thunk.ax
        add     eax, OFFSET FLAT:_GLOBAL_OFFSET_TABLE_  # EAX points to the GOT
        mov     eax, 305441742                  # overwrite with the return value
        pop     ebp
        ret

__x86.get_pc_thunk.ax:          # this is the helper function gcc calls
        mov     eax, DWORD PTR [esp]
        ret

“thunk”返回它的返回地址。即 call 之后的指令地址. .ax name 表示在 EAX 中返回。现代 GCC 可以选择任何寄存器;传统上,32 位 PIC 基址寄存器始终是 EBX,但现代 GCC 在避免额外保存/恢复 EBX 时选择了一个 call-clobbered 寄存器。

有趣的事实:call +0; pop eax会更有效率,并且在每个调用站点只增加 1 个字节。您可能认为这会使返回地址预测器堆栈失衡,但实际上 call +0在大多数 CPU 上都是特殊情况,不会这样做。 http://blog.stuffedcow.net/2018/04/ras-microbenchmarks/#call0 . (call +0 表示 rel32 = 0,因此它调用下一条指令。不过,NASM 不是这样解释该语法的。)

除非需要,否则 clang 不会生成 GOT 指针,即使在 -O0 处也是如此。 .但它使用 call +0 ; pop %eax : https://godbolt.org/z/GFY9Ht

关于c - 在 gcc 32 位代码中未定义的对 `_GLOBAL_OFFSET_TABLE_' 的引用,用于一个简单的函数,独立的操作系统,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59070109/

相关文章:

C 错误 : array type has incomplete element type

assembly - 我们怎样才能知道这个处理器支持多少内存呢?

c - 中断服务例程不会跳回到 ARM Cortex M0 上的中断处理程序

c - 我的 C 程序出现段错误错误

c - 一步一步(F10)运行正常,但(ctrl+F5)运行错误,有解决办法吗? C

assembly - Intel x86汇编手册中的00+是什么意思?

assembly - 如何用汇编语言创建文本编辑器?

c++ - LD_LIBRARY_PATH : Cannot open shared object file

c - GCC 警告识别包含指针的复制结构

c - C语言中如何通过内存地址映射函数名和行号?