C全局静态变量初始化是由链接器完成的吗?

时间:2017-07-07 04:43:29

标签: c gcc linker symbol-table

让我们说:

在f1.c

#include <stdio.h>
static int x = 10;

void f1() {
  printf("f1.c : %d\n", x);
}

的main.c

extern void f1();
int main(int argc, char **argv) {
  f1();
  return 0;
}

我们将编译并读取两个ELF文件符号表(rel.ELF和exec ELF):

$> gcc -c *.c
$> readelf -s f1.o | grep x
      Num:    Value          Size Type    Bind   Vis      Ndx Name
        5: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    3 x
$> gcc *.o
$> readelf -s a.out | grep x
      Num:    Value          Size Type    Bind   Vis      Ndx Name
       38: 0000000000601038     4 OBJECT  LOCAL  DEFAULT   25 x

我可以看到全局静态变量Valuex的{​​{1}}(也称为地址)读取可重定位目标文件0000000000000000
这意味着我们还没有初始化它,因为它仍然是一个rel。 ELF目标文件和链接器将处理这个问题。

那么我的问题是,如果链接是在f1.o链接后在已知地址将x设置为值10的链接器,它是如何做到的?链接器在哪里获取信息 将值设置为10 ,谁提供此信息(0000000000601038?)?

2 个答案:

答案 0 :(得分:5)

0000000000000000(在目标文件f1.o中)是相对地址(静态变量),因此是偏移量,该文件还包含{{ 3}}与之相关的指令。获取参数x以进行打印的代码也有一些重定位(在某些加载机器指令上)。

在该目标文件中,您可能有.data部分。该部分应以包含10的单词(在f1.o中观察到0偏移)开头。

阅读更多关于relocation的信息(我推荐Levine的linkers一书)。链接过程(获取Linkers and loaders可执行文件)是处理重定位指令。阅读更多关于ELF格式的信息,从ELF开始(在阅读elf(5) wikipage之后)。另请参阅ELF规范(适用于Linux x86-64,请参阅ABI来自here),其中详细说明了可能的重定位指令。

您可能希望使用f1.c编译gcc -Wall -S -fverbose-asm -O1 f1.c,然后查看发出的汇编程序文件f1.s

您可能还想使用thisreadelf(1)等各种工具检查目标文件f1.o和ELF可执行文件a.out。两者都接受了许多选项(特别是-r选项objdump以显示重定位指令。)

objdump(1)Dynamic linking libc.*.so)在ELF可执行文件中引入了一些额外的复杂性。另请参阅C standard library(在运行时开始时执行某些链接作业)和ld-linux(8)。您可能还想阅读Drepper的vdso(7)论文。

免费提供的教科书How To Write Shared Libraries也值得一读(它解释了Operating Systems: Three Easy Pieces是什么以及如何执行)。

答案 1 :(得分:0)

存储具有特定值的静态存储持续时间变量的此段称为 # | Event | Time 1 A 22:00:00 2 B 22:00:10 3 B 22:00:20 4 B 22:00:30 5 C 22:00:40 6 B 22:00:10 7 B 22:00:20 8 B 22:00:30 9 A 22:00:40 (这是ELF标准使用的名称,但其他链接器也倾向于使用相同的名称)。

如何设置这些变量取决于目标系统。

  • 在基于RAM的系统(例如PC)上,整个 # | Event | Time | Duration 1 A 22:00:00 NA 2 B 22:00:10 20 Secs 3 B 22:00:20 NA 4 B 22:00:30 NA 5 C 22:00:40 NA 6 B 22:00:10 20 Secs 7 B 22:00:20 NA 8 B 22:00:30 NA 9 A 22:00:40 NA 段作为可执行文件的一部分提前初始化,并与程序一起加载到RAM中。
  • 在基于ROM的系统(例如带闪存的微控制器)上,.data无法提前初始化。而是在调用main()之前将其从ROM复制到RAM,通过一些启动代码(&#34; CRT&#34;)。所以它实际上是在运行时设置的,这意味着在这样的系统上程序启动时始终存在延迟。为了摆脱延迟,通常会有一个非标准的启动替代方案(&#34; minimal&#34;),它会完全跳过静态存储持续时间变量的初始化。