我希望编译器输出一个文件,其中包含指向它正在编译的源代码中的所有全局变量的指针,以及它们的大小。
这可能吗?有没有办法在任何c编译器中执行此操作?
答案 0 :(得分:3)
像地图文件?这将显示全局和静态分配的位置,但不会显示它们所指向的位置。大多数编译器(链接器)将自动输出一个或使用简单的语句。只需在文档中搜索地图文件即可。
答案 1 :(得分:1)
虽然输出此数据不需要编译器,但大多数链接器可以转储此信息。例如,Microsoft的链接器mapfile包含可执行文件/ dll中的所有公共符号以及它们相对于它们放入的部分(只读,读写,代码,零初始化等)的地址。大小可以从虽然它主要是近似值。
您也可以找到一种方法来检查为可执行文件生成的调试符号,因为这正是调试器必须要做的事情。
答案 2 :(得分:1)
通常你会从链接器而不是编译器得到这个 - 链接器就是为事物分配地址的东西。大多数链接器可以生成一个映射文件,该文件将包含全局变量和函数的地址(以及它创建的可执行文件中的任何其他符号)。由你来决定哪些是哪个。我所看到的所有内容都包含了一些内容,但确切的格式因所涉及的链接器而异。
答案 3 :(得分:1)
此信息可在二进制文件的符号表中找到,但它可能并不代表您的预期。
编译器获取一个或多个源文件,将代码编译为目标代码,并生成目标文件(Unix上为.o,Windows上为.obj)。在源文件中引用的所有变量和函数都在符号表中提及。源文件中定义的变量和函数具有特定的地址和大小,而源文件中未定义的符号标记为未定义,必须稍后链接。所有符号都是相对于特定部分列出的。常用部分是可执行代码的“.text”,“。bss”表示程序启动时初始化为零的变量,“.data”表示用非零值初始化的变量。
链接器接受一个或多个目标文件,组合这些部分(将每个目标文件中的所有代码和数据放入代码和数据的一个大部分),并写入输出文件。此输出文件可以是可执行文件,也可以是共享库。磁盘上的可执行文件仍然没有每个变量的指针;它仍然存储从该部分开头到变量的偏移量。
运行可执行文件时,操作系统的动态加载程序将读取可执行文件,查找每个部分,并为该部分分配内存。 (它也可能在每个部分设置不同的权限 - “。text”段通常标记为只读,并且(在支持它的处理器上)数据段有时被标记为不可执行。)变量获取指针 - 当代码需要访问某个特定变量时,它会将该部分开头的地址添加到该部分开头的偏移处以获取指针。
您可以使用各种工具来调查每个二进制文件的符号表。 GNU工具链的objdump
(在Linux上使用)就是这样一种工具。
对于一个简单的C hello-world程序:
#include <stdio.h>
const char message[] = "Hello world!\n";
int main(int argc, char ** argv) {
printf(message);
return 0;
}
我在Linux机器上编译(但不要链接):
$ gcc -c hello.c -o hello.o
现在我可以查看符号表:
$ objdump -t hello.o
hello.o: file format elf32-i386
SYMBOL TABLE:
00000000 l df *ABS* 00000000 hello.c
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .rodata 00000000 .rodata
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .comment 00000000 .comment
00000000 g O .rodata 0000000e message
00000000 g F .text 0000002b main
00000000 *UND* 00000000 puts
第一列是每个符号的地址,相对于该部分的开头。每个符号都有各种标志,一些符号用作工具链和调试器其余部分的提示。 (如果我使用调试符号构建,我会看到许多条目也专门用于它们。)我的简单程序只有一个变量:
00000000 g O .rodata 0000000e message
第五列告诉我符号message
的大小为0xe - 14个字节。