我有一个C程序,其中有一个全局变量和一个局部变量。我的问题是关于readelf的。以下是我的问题: 1. 当我使用“readelf --symbols”获取地址转储时,我得到了一个全局变量的地址,该地址与我运行程序时打印的地址相同。在我的程序运行或加载之前,readelf如何知道绝对地址? 2. 为什么没有关于局部变量符号的信息?我只能看到全局变量的符号。
readelf如何在我的程序运行或被加载之前就知道绝对地址?
因为链接器完成工作时,计算出来的全局变量地址正是程序加载器在运行时必须放置该变量的地址。链接器的工作主要是将信息放入可执行文件中,告诉程序加载器在内存中映射符号的位置。
为什么本地变量没有符号信息?
您的程序中可能有三种类型的“本地”变量。
main.c
static int static_filescope_i = 1;
int f()
{
static int static_local_i = 2;
return static_local_i;
}
int g()
{
int automatic_i = 3;
return automatic_i;
}
int global_i = 4;
int main()
{
return global_i + f() + g() + static_filescope_i;
}
像automatic_local_i
这样的自动变量会在程序每次进入定义它的块时在堆栈上创建,并在离开该块时停止存在。这样的变量不占用可执行文件中的存储空间,因此在符号表中不表示。
像static_filescope_i
这样的变量通常被称为静态全局变量,以区别于像static_local_i
这样的变量。 static_local_i
不能在定义它的块之外看到。 static_filescope_i
可以在同一目标文件(main.o
)中定义的任何函数中看到,但不能在其他目标文件中看到:它在main.o
中是全局的,但在整个程序中是局部于该目标文件。
当程序首次使用变量时,static_filescope_i
和static_local_i
都必须具有其初始值,并保留其任何值或分配给它的任何新值,直到下一次使用 - 跨函数调用,直到程序结束。这意味着这些变量需要在可执行文件中存储,而不是在堆栈上,并且它们可能或可能不会在符号表中表示。
global_i
当然是整个程序的全局变量:它可以在main.o
和我们可能链接到main.o
的任何其他文件中看到。
如果我们使用默认选项(无优化)编译main.c
:
$ gcc -c main.c
$ readelf -s main.o | grep automatic_i
$
...没有符号automatic_i
。
$ readelf -s main.o | grep global_i
12: 0000000000000004 4 OBJECT GLOBAL DEFAULT 3 global_i
...一个用于表示 global_i
的全局符号。
$ readelf -s main.o | grep static_filescope_i
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 static_filescope_i
...一个本地符号,表示static_filescope_i
$ readelf -s main.o | grep static_local_i
6: 0000000000000008 4 OBJECT LOCAL DEFAULT 3 static_local_i.1833
这里,GLOBAL
表示可以被链接器看到,而 LOCAL
表示不能被链接器看到。
因此,为了将 main.o
与其他目标文件或库链接以创建可执行文件,static_filescope_i
和 static_local_i
可能不存在。
但这并不意味着它们在目标文件中完全没有用处。它们有助于调试,有助于研究可执行文件的静态存储是由什么组成的,就像我们现在正在做的那样。
但它们对于链接器没有用处,因此,如果您使用任何优化级别 > 0 编译 main.c
,则编译器将假定您想要的对象代码不是用于调试或研究目的,并且它不会生成任何本地符号:
$ gcc -O1 -c main.c
$ readelf -s main.o | grep static_local_i
$ readelf -s main.o | grep static_filescope_i
$ readelf -s main.o | grep global_i
11: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_i
...只有global_i
保留。
这就解释了为什么你看不到任何“局部”符号。你的自动变量从未出现在符号表中。只有当你禁用所有优化时,你的静态变量才会出现在符号表中。