【问题标题】:undefined reference to _GLOBAL_OFFSET_TABLE_ (only when generating binaries)对 _GLOBAL_OFFSET_TABLE_ 的未定义引用(仅在生成二进制文件时)
【发布时间】:2018-01-07 09:48:35
【问题描述】:

这就是问题所在:
当我使用 ld 在 C 中链接我的脚本时,当我在 ld 中生成 elf32-i386 文件作为输出格式时,将其作为 LD 脚本中的 OUTPUT_FORMAT() ,我没有任何错误,但是如果我尝试将其放入最后OUTPUT_FORMAT() "binary" 或尝试输出带有 .bin 扩展名的文件时,我会遇到以下混合错误:

kernel.o: In function `k_main':
kernel.c:(.text+0xe): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_clear_screen':
kernelutils.c:(.text+0xc): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_clear_screen_front':
kernelutils.c:(.text+0x56): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_printf':
kernelutils.c:(.text+0xa0): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_sleep_3sec':
kernelutils.c:(.text+0x152): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelmalloc.o:kernelmalloc.c:(.text+0xc): more undefined references to `_GLOBAL_OFFSET_TABLE_' follow

这不仅发生在编译特定脚本时,所有尝试使用 ld 链接的脚本,或者由于调用 ld 的 gcc,都会在尝试获取具有 .bin 扩展名的二进制文件时死掉。
当显示其中一个可执行文件的符号(上面输出中的 kernel.o)时,我看到符号 _GLOBAL_OFFSET_TABLE_ 没有定义,最可怕的部分是,在上面的错误输出中返回错误的所有函数都删除了它们的符号,这是 nm 输出:

cristian@mymethodman:~/Desktop/kernel/0.0.3/Archivos$ nm kernel.o
         U _GLOBAL_OFFSET_TABLE_
         U k_clear_screen
         U k_clear_screen_front
00000000 T k_main
         U k_malloc
         U k_printf
         U k_sleep_3sec
00000000 T __x86.get_pc_thunk.bx

我该如何解决这个问题?我将保留下面的链接器脚本以确保它是 .ld 文件的问题,同时具有“获取 elf”和“获取二进制”版本。提前致谢!

Ld 脚本:
获取二进制文件:

ENTRY(loader)
OUTPUT_FORMAT(binary)

SECTIONS {
   /* The kernel will live at 3GB + 1MB in the virtual
      address space, which will be mapped to 1MB in the
      physical address space. */
   . = 0xC0100000;

   .text : AT(ADDR(.text) - 0xC0000000) {
       *(.text)
       *(.rodata*)
   }

   .data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) {
       *(.data)
   }

   .bss : AT(ADDR(.bss) - 0xC0000000) {
       _sbss = .;
       *(COMMON)
       *(.bss)
       _ebss = .;
   }
}

要获得 ELF:

ENTRY(loader)
OUTPUT_FORMAT(elf32-i386)

SECTIONS {
   /* The kernel will live at 3GB + 1MB in the virtual
      address space, which will be mapped to 1MB in the
      physical address space. */
   . = 0xC0100000;

   .text : AT(ADDR(.text) - 0xC0000000) {
       *(.text)
       *(.rodata*)
   }

   .data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) {
       *(.data)
   }

   .bss : AT(ADDR(.bss) - 0xC0000000) {
       _sbss = .;
       *(COMMON)
       *(.bss)
       _ebss = .;
   }
}

正如您所见,两者之间只会更改 OUTPUT_FORMAT() 行。

【问题讨论】:

  • 看起来您的主机 GCC 环境(可能是 Ubuntu 16.04+ 或 Debian 9+ 之类的较新版本)正在生成与位置无关的代码。当您使用 GCC 编译时,您可能想尝试使用 -fno-pic 编译所有文件。这是使用 GCC 交叉编译器进行操作系统开发的一个很好的理由。您可以在此处阅读有关创建一个的信息:wiki.osdev.org/GCC_Cross-Compiler
  • 谢谢,我会构建交叉编译器
  • 附带说明 - 为什么要生成二进制文件?我知道您以前使用的是兼容多重引导的加载程序。使用二进制文件可能会给您带来问题,除非您现在正在编写自己的引导加载程序,或者您已经更改了多重引导标头以支持 a.out kludge。
  • @MichaelPetch 我有一个类似的问题,我正在尝试调试。我将-fpie 与GCC 一起使用,但链接器正在发出与GOT 相关的重定位条目。我确定我使用了一组不正确的输入标志来发生这种情况(即使将二进制文件输入 IDA Pro,它也会向我报告二进制文件使用“非标准”重定位!)。有什么想法吗?与 OP 类似,_GLOBAL_OFFSET_TABLE_ 未定义且没有 .got 部分。实际上,从参数集中删除 -fpic 阻止了 GOT 相对重定位(现在是 ABS)(但仍然没有 .got 部分)。你介意帮我理解一下吗?

标签: gcc linker linker-errors ld osdev


【解决方案1】:

您的工具链可能默认生成与位置无关的可执行文件 (PIE)。尝试使用gcc -fno-pie 编译。

如果您出于安全原因想要保留 PIE,则需要更复杂的链接器脚本和执行初始重定位的东西(例如动态链接器,但也可以使用更简单的构造)。

【讨论】:

  • 我遇到了类似的问题。我为 ARM 目标编写了一个运行时 ELF 重定位器,链接器将发出与 GOT 相关的重定位条目。与此处显示的方式相同, 符号_GLOBAL_OFFSET_TABLE_,但它未定义且未关联到任何部分(因为没有发出.got 部分)。我明确地将代码编译为 PIE(例如-fpie)。生成 PIE 代码时需要哪些更复杂的链接描述文件?如果重要的话,也在使用 GCC。删除 -fpie 可以防止这种情况发生。这是为什么呢?
猜你喜欢
  • 1970-01-01
  • 2011-09-21
  • 1970-01-01
  • 2011-11-30
  • 1970-01-01
  • 1970-01-01
  • 2013-10-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多