【问题标题】:Why does libgcc use global offset table?为什么 libgcc 使用全局偏移表?
【发布时间】:2013-11-19 10:17:23
【问题描述】:

我正在尝试为 i386-elf 目标构建 gcc 4.6。

我的问题如下。在编译libgcc 时,我得到了使用全局偏移表的output-file _fixunsdfdi.o

在将“double”转换为“unsigned long long”时使用该函数。 当我查看程序集时,它看起来像是放置在 GOT 中的 Wtype_MAXp1_F - 但为什么呢?

我配置了

--target=i386-elf  --enable-languages=c --disable-nls --disable-libssp --disable-libquadmath --enable-shared=no  --enable-static=yes


//Code for fixunsdfdi (from libgcc2.c)
#if defined(L_fixunsdfdi) && LIBGCC2_HAS_DF_MODE
UDWtype __fixunsdfDI (DFtype a)
{
/* Get high part of result.  The division here will just moves the radix
 point and will not cause any rounding.  Then the conversion to integral
 type chops result as desired.  */
const UWtype hi = a / Wtype_MAXp1_F;

/* Get low part of result.  Convert `hi' to floating type and scale it back,
 then subtract this from the number being converted.  This leaves the low
 part.  Convert that to integral type.  */
const UWtype lo = a - (DFtype) hi * Wtype_MAXp1_F;

/* Assemble result from the two parts.  */
return ((UDWtype) hi << W_TYPE_SIZE) | lo;
}
#endif


//Dump of the output
Class:                             32-bit
Data:                              Little Endian
Header version:                    1[Current Version]
OS/ABI:                            0[UNIX System V ABI]
Type:                              1[REL (Relocatable file)]
Machine:                           0003h[Intel Architecture EM_386]
File version:                      1[Current Version]
Entry point address:               00000000h
Start of program headers:          0 (bytes into file)
Start of section headers:          35496 (bytes into file)
Flags:                             0
Size of this header:               52 (bytes)
Size of program headers:           0 (bytes)
Number of program headers:         0
Size of section headers:           40 (bytes)
Number of section headers:         15
Section header string table index: 12

[file offset:00008AA8h]Section Headers:
[Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
[0]                    NULL            00000000 000000 000000 00     0  0   0
[1]  .text             PROGBITS        00000000 000034 000067 00 AX  0  0   4
[2]  .rel.text         REL             00000000 008E18 000018 08     13 1   4
[3]  .data             PROGBITS        00000000 00009C 000000 00 WA  0  0   4
[4]  .bss              NOBITS          00000000 00009C 000000 00 WA  0  0   4
[5]  .stab             PROGBITS        00000000 00009C 0004D4 0C     7  0   4
[6]  .rel.stab         REL             00000000 008E30 000030 08     13 5   4
[7]  .stabstr          STRTAB          00000000 000570 00844A 00     0  0   1
[8]  .rodata.cst4      PROGBITS        00000000 0089BC 000008 04 A   0  0   4
[9]  .comment          PROGBITS        00000000 0089C4 000028 01     0  0   1
[10] .eh_frame         PROGBITS        00000000 0089EC 000054 00 A   0  0   4
[11] .rel.eh_frame     REL             00000000 008E60 000008 08     13 10  4
[12] .shstrtab         STRTAB          00000000 008A40 000067 00     0  0   1
[13] .symtab           SYMTAB          00000000 008D00 0000E0 10     14 12  4
[14] .strtab           STRTAB          00000000 008DE0 000038 00     0  0   1

[file offset:00008E18h]Relocation section '.rel.text' contains 3 entries:
Type: REL
   Num:   Offset     Info    Type            Sym.Value Addend   To->Sym. Name
0:       0000000E  00000D0A R_386_GOTPC      00000000  00000003 _GLOBAL_OFFSET_TABLE_
1:       0000001A  00000709 R_386_GOTOFF     00000000  00000000 .LC0
2:       0000004A  00000809 R_386_GOTOFF     00000004  00000000 .LC1

[file offset:00008E30h]Relocation section '.rel.stab' contains 6 entries:
Type: REL
Num:   Offset     Info    Type            Sym.Value Addend   To->Sym. Name
0:       00000014  00000201 R_386_32         00000000  00000000 .text
1:       00000020  00000201 R_386_32         00000000  00000000 .text
2:       00000434  00000C01 R_386_32         00000000  00000000 __fixunsdfdi
3:       0000044C  00000201 R_386_32         00000000  00000000 .text
4:       000004C4  00000201 R_386_32         00000000  00000067 .text
5:       000004D0  00000201 R_386_32         00000000  00000067 .text

[file offset:00008E60h]Relocation section '.rel.eh_frame' contains 1 entries:
Type: REL
Num:   Offset     Info    Type            Sym.Value Addend   To->Sym. Name
0:       00000020  00000202 R_386_PC32       00000000  00000000 .text

[file offset:00008D00h]Symbol table '.symtab' contains 14 entries:
Num[h]:    Value  Size Type    Bind   Vis      Ndx Name
000000: 00000000 0000  NOTYPE  LOCAL           UND 
000001: 00000000 0000  FILE    LOCAL           ABS libgcc2.c
000002: 00000000 0000  SECTION LOCAL           1   .text
000003: 00000000 0000  SECTION LOCAL           3   .data
000004: 00000000 0000  SECTION LOCAL           4   .bss
000005: 00000000 0000  SECTION LOCAL           8   .rodata.cst4
000006: 00000000 0000  SECTION LOCAL           10  .eh_frame
000007: 00000000 0000  NOTYPE  LOCAL           8   .LC0
000008: 00000004 0000  NOTYPE  LOCAL           8   .LC1
000009: 00000000 0000  SECTION LOCAL           5   .stab
00000A: 00000000 0000  SECTION LOCAL           7   .stabstr
00000B: 00000000 0000  SECTION LOCAL           9   .comment
00000C: 00000000 0067  FUNC    GLOBAL          1   __fixunsdfdi
00000D: 00000000 0000  NOTYPE  GLOBAL          UND _GLOBAL_OFFSET_TABLE_

我不希望创建全局偏移表,因为我在其他地方不需要它,而且我正在处理的项目中的链接器目前不支持它。

Q1:为什么在全局偏移表中使用符号?

Q2:有没有办法避免使用全局偏移表? 有什么帮助吗?

【问题讨论】:

  • 请提供更多信息:主机、命令行和编译报告的错误。
  • 主机是-i686-pc-cygwin。我在日志中找不到任何错误。
  • 很遗憾,这个问题已经被忽视到可能不再相关的地步,我也很遗憾地通知您,如果不知道Wtype_MAXp1_F 的确切定义,就无法回答。使用-save-temps 捕获_fixunsdfdi.o 的预处理器输出可能是最简单的。 (我想我可以自己为 i386-elf 构建这个版本的 gcc 并找出答案,但这比我今天的时间还多。)
  • 如果您将整个.s 文件(来自-save-temps)逐字粘贴到问题中也会有所帮助。
  • 这...听起来像是一个错误,可能在构建系统中。或者您可能需要专门要求非 PIC 输出?

标签: c


【解决方案1】:

好的,我不确定我是否完全理解,但这里是......

Q1:为什么在全局偏移表(GOT)中使用符号?

之所以使用 GOT,是因为编译器、链接器和加载器的工作方式,不可能知道每个对象在运行时会在内存中的什么位置结束。编译器正确的步骤一次只能看到一个模块,因此它无法判断每个(外部)函数和全局变量将从哪里被引用。相反,它会为每一个创建一个符号,以便在链接时解析。对于静态函数/变量,它生成直接访问它们的代码(它取决于平台:在 Linux/x86_64 中使用程序计数器的偏移量)。对于外部,它通过运行时数据结构生成间接访问代码:GOT。

在链接时,您可以知道哪些模块调用了哪些符号,因此链接器的工作是确保所有符号(在您的程序中或在其外部依赖项中)都已解析,这意味着它们在组成您的程序的多个模块。对于在您自己的代码中定义(或静态链接)的对象,可以知道它在二进制文件中的位置(ELF/COFF/PE/等),但在加载之前您不知道它们在内存中的位置。链接器还将所需的共享库和(可选)它们在运行时的可能位置写入二进制文件。

当你运行你的程序时,the loader!将尝试查找所有依赖项(共享库),然后作为进程启动的一部分,将构建 GOT 结构。它会将您的程序代码从二进制的文本部分加载到内存中,并且对于每个符号,它将在 GOT 中创建一个条目,其中包含在何处可以找到它的实际内存地址。然后它将对每个共享库执行相同的操作。还值得注意的是,一些库可能会保留独立于 GOT 的内部偏移表。这样,您可以多次定义相同的符号。这是Window SxS 发生的例子! (微软对 DLL 地狱问题的回答)。

从您的程序的角度来看,每次访问外部符号需要 2 次内存访问(间接)。如果符号代表一个函数,还有另一个结构PLT,即一个跳转表,每个地方都有GOTO指令。

Q2:有没有办法避免使用全局偏移表?有什么帮助吗?

是的,最简单的方法是找出在它们定义的模块之外不需要访问的函数/全局变量,并将它们声明为静态。这样,编译器就会知道它可以生成直接访问代码来找到它们(程序计数器的偏移量是它在 Linux/x86_64 中完成的方式,但它取决于平台)。

我读到在大型项目中,有一个专门的构建团队可以识别紧密耦合的模块集并将它们集成到一个大 C 文件中,以最大限度地减少通用产品中暴露符号的数量,但我从来没有亲眼所见。

还有一些链接器选项可以帮助您避免暴露符号,但据我所知,它们只是语法糖。 GOT 和 PLT 仍在使用中,只是您将其内容设为不透明。

Ulrich Drepper 的How to Write Shared Libraries 是这个主题的一个很好的资源!

【讨论】:

  • 嗯,我不认为这些信息与这里的问题特别相关;我认为 OP 完全了解 GOT 是什么,并且考虑到他只尝试编译静态代码并且他的链接器不支持 GOT,很明显为什么他既不需要也不需要。
  • 我现在正在重读原始问题,我认为您可能是对的。我首先想到的问题是 GOT 中的一般符号是什么以及为什么。现在我意识到问题是为什么这个特定的符号最终会出现在 GOT 中(从上下文来看......我认为不应该)。那么,我们该何去何从?我应该删除还是编辑我的答案。请指教。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-30
  • 2018-09-23
  • 2021-04-07
  • 2021-10-04
  • 2012-03-30
  • 2021-11-26
相关资源
最近更新 更多