《链接器之Map文件与符号表》由会员分享,可在线阅读,更多相关《链接器之Map文件与符号表(11页珍藏版)》请在金锄头文库上搜索。
1、一、一、map、全局符号及静态符号、全局符号及静态符号一般的大型工程都会在生成可执行文件的同时让链接器生成一个 map 文件,从而大致查看一下可执行文件中符号的内存布局以及从哪里引入可执行文件。这个通常对于小型工程是作用不大,因为代码就那么多,随便 grep 一下就知道符号定义位置了。但是对于一些大型工程或者涉及了比较多的第三方库、或者涉及了比较多的功能模块的时候,就需要知道这些符号是在哪里定义,或者说如果一个符号引用了但是没有知道函数定义,此时也需要找到这个符号是哪个模块引入的,为什么需要,所以需要一些通用的(形式化)的方法来搜索这些符号,而 map 文件就是一个比较好的切入点。但是 map
2、 符号并不是万能的,它只能列出参与链接的全局变量的位置以及在哪个模块,对于一些静态变量,map 文件中并不能体现它们,而在没有特殊声明的情况下,可执行文件中将会包含静态符号在符号表中,所以有时候我们只能依赖可执行文件本身里面的符号表来猜测一个符号的定义位置。说起静态符号,还有就是它是如何保证它只在一个编译模块中可见和被引用,而对其它模块不可见?二、二、map 文件相关文件相关对于 ld 程序来说,生成 map 文件可以使用-Map=mapfile 来指示链接器来生成一个可执行文件使用的 map 文件。在内核的构建过程中,也会生成一个 System.map 文件来表示内核中各个符号在内核中位置,
3、但是这个文件并不是通过 ld 的-Map 选项生成,而是使用了 nm 和 grep 工具来手动生成的,具体的文件文件及相关说明在 linux-2.6.37.1scriptsmksysmap 文件中。我们这里只是结合 ld 的源代码来看一下这个 Map 文件是如何生成的。1、map 文件生成代码文件生成代码对于 map 文件的生成,在 ld 的源代码中,名字也比较直观,就是通过 lang_map 函数来完成的,它的主要相关流程为fprintf (config.map_file, _(“nLinker script and memory mapnn“);if (! link_info.reduce
4、_memory_overheads)obstack_begin (for (p = link_info.input_bfds; p != (bfd *) NULL; p = p-link_next)bfd_map_over_sections (p, init_map_userdata, 0);bfd_link_hash_traverse (link_info.hash, sort_def_symbol, 0);lang_statement_iteration +;print_statements ();其中的主要准备工作由 bfd_link_hash_traverse (link_info.h
5、ash, sort_def_symbol, 0)语句完成,它遍历整个链接过程中所有的符号表,然后对其中的每个符号执行 sort_def_symbol 函数,这个函数的功能主要是将这个符号追加到符号定义节的 userdata 链表的最后,供之后执行的print_statements 函数可以在遍历各个输入节的时候打印输入节的 map 信息。这里对于 bfd_link_hash_traverse (link_info.hash, sort_def_symbol, 0);语句实现要注意两个细节:、输出符号性质、输出符号性质在 sort_def_symbol 函数的定义中,它只会追加类型为 bfd_l
6、ink_hash_defined 和bfd_link_hash_defweak 属性的符号(代码不再粘贴,代码比较直观,贴出来影响阅读),其它的一概忽略,这也就意味着所有的局部变量符号没有机会在 map 文件中体现。、符号遍历规则、符号遍历规则符号遍历是通过 bfd_link_hash_traverse 函数遍历,这个遍历的符号没有任何逻辑规律,它们只是依赖底层 hash 算法的选择而被放在不同的 bucket 中,这会导致对于每个输入节来说,它即将输出的定义符号列表并不一定是按照它们在内存中的逻辑地址位置排列的。2、验证代码、验证代码tsecerHarry maporder$ cat map
7、order.c static int foo;int main()extern int bar(void);return foo + bar();int bar(void)return 0x11111111; tsecerHarry maporder$ gcc maporder.c -Wl,-Map=map.txttsecerHarry maporder$ grep -e main -e bar map.txt 0x00000000080482c4 _libc_start_mainGLIBC_2.00x00000000080483ab bar0x0000000008048394 maintse
8、cerHarry maporder$ ld -VGNU ld version 2.19.51.0.14-34.fc12 20090722Supported emulations:elf_i386i386linuxelf_x86_64tsecerHarry maporder$ 可以看到,其中 bar 的逻辑地址要比 main 的逻辑地址高,但是它在 map 文件中出现的顺序要比 main 早,所以说同一个节内符号在 map 文件中的出现顺序和它们的逻辑地址无关,大家不要依赖这个顺序。3、ld2.20 版本对版本对 map 文件的优化文件的优化之前验证的代码可以看到,一个节内部符号出现顺序和逻辑地
9、址无关,这个属性在链接器的2.20 版本中进行了修改(优化),优化的结果就是 map 文件中同一个节中定义符号在 map 文件中出现顺序按照逻辑地址排序,这个代码合入时间比较晚,大致是在 2009 年 9 月的 2.20 版本总加入,我在网上也搜索到了这个补丁的讨论邮件,为了防止链接地址失效,这里还是把邮件内容拷贝一份过来,原始地址为(http:/ Tristan Gingold * ld.h (fat_user_section_struct): Add map_symbol_def_count field. * ldlang.c (hash_entry_addr_cmp): New func
10、tion. (print_all_symbols): Sort the symbols by address before printing them.RCS file: /cvs/src/src/ld/ld.h,vretrieving revision 1.43diff -u -p -r1.43 ld.h- ld.h 18 Mar 2009 11:27:18 -0000 1.43+ ld.h 8 Jul 2009 09:08:52 -0000 -114,6 +114,7 typedef struct fat_user_section_struct list of hash table ent
11、ries for symbols defined in this section. */struct map_symbol_def *map_symbol_def_head;struct map_symbol_def *map_symbol_def_tail;+ unsigned long map_symbol_def_count; fat_section_userdata_type;#define get_userdata(x) (x)-userdata) Index: ldlang.c = RCS file: /cvs/src/src/ld/ldlang.c,v retrieving re
12、vision 1.311 diff -u -p -r1.311 ldlang.c - ldlang.c 25 Jun 2009 13:18:46 -0000 1.311 + ldlang.c 8 Jul 2009 09:08:53 -0000 -1988,6 +1988,7 init_map_userdata (bfd *abfd ATTRIBUTE_U ASSERT (get_userdata (sec) = NULL); get_userdata (sec) = new_data; new_data-map_symbol_def_tail = + new_data-map_symbol_d
13、ef_count = 0; static bfd_boolean -2015,6 +2016,7 sort_def_symbol (struct bfd_link_hash_en def-entry = hash_entry; *(ud-map_symbol_def_tail) = def; ud-map_symbol_def_tail = + ud-map_symbol_def_count+; return TRUE; -3940,18 +3942,48 print_one_symbol (struct bfd_link_hash_e return TRUE; +static int+has
14、h_entry_addr_cmp (const void *a, const void *b)+ const struct bfd_link_hash_entry *l = *(const struct bfd_link_hash_entry *)a;+ const struct bfd_link_hash_entry *r = *(const struct bfd_link_hash_entry *)b;+ if (l-u.def.value u.def.value)+ return -1;+ else if (l-u.def.value r-u.def.value)+ return 1;+
15、 else+ return 0;+static voidprint_all_symbols (asection *sec)struct fat_user_section_struct *ud = get_userdata (sec);struct map_symbol_def *def;+ struct bfd_link_hash_entry *entries;+ unsigned int i;if (!ud) return;*ud-map_symbol_def_tail = 0;- for (def = ud-map_symbol_def_head; def; def = def-next)- print_one_symbol (def-entry, sec);+ /* Sort the symbols by address. */+ entries = obstack_alloc (+ for (i = 0, def = ud-map_symbol_def_head; def; def