hse365平台-mobile365官网是多少-英国365网站最近怎么了

gcc编译链接之Map文件分析

gcc编译链接之Map文件分析

最近在研究MiCO OS项目的时候,发现编译目录build下有一个xxxx.map文件,打开一看,感觉都是一些内存段和符号信息,由此想到应该是编译链接过程中输出的一些信息。之前没有接触过,今天就来学习一下map文件是个什么东西、有什么作用。

map文件片段:

Allocating common symbols

Common symbol size file

errno 0x4 d:/programs/micoder/compiler/arm-none-eabi-5_4-2016q2-20160622/win32/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/armv7e-m/fpu\libc_nano.a(lib_a-reent.o)

Discarded input sections

.text 0x00000000 0x0 ./build/mico-iot-module@mx1290@moc/libraries/App_IoT_Module.a(app_main.o)

.data 0x00000000 0x0 ./build/mico-iot-module@mx1290@moc/libraries/App_IoT_Module.a(app_main.o)

.bss 0x00000000 0x0 ./build/mico-iot-module@mx1290@moc/libraries/App_IoT_Module.a(app_main.o)

.comment 0x00000000 0x6f ./build/mico-iot-module@mx1290@moc/libraries/App_IoT_Module.a(app_main.o)

.ARM.attributes

0x00000000 0x39 ./build/mico-iot-module@mx1290@moc/libraries/App_IoT_Module.a(app_main.o)

.text 0x00000000 0x0 ./build/mico-iot-module@mx1290@moc/libraries/App_IoT_Module.a(mqtt_interface.o)

.data 0x00000000 0x0 ./build/mico-iot-module@mx1290@moc/libraries/App_IoT_Module.a(mqtt_interface.o)

.bss 0x00000000 0x0 ./build/mico-iot-module@mx1290@moc/libraries/App_IoT_Module.a(mqtt_interface.o)

.comment 0x00000000 0x6f ./build/mico-iot-module@mx1290@moc/libraries/App_IoT_Module.a(mqtt_interface.o)

.ARM.attributes

1 什么是Map文件?

简单来说,map文件就是通过编译器编译之后,生成的程序、数据及IO空间信息的一种映射文件,里面包含函数大小,入口地址等一些重要信息。从map文件我们可以了解到:

程序各区段的寻址是否正确

程序各区段的size,即目前存储器的使用量

程序中各个symbol的地址

各个symbol在存储器中的顺序关系(这在调试时很有用)

各个程序文件的存储用量

2 如何生成?

生成map文件是链接器ld的功能,有两种方式可以生成map文件:

通过gcc参数-Wl,-Map,:

gcc -o helloworld helloworld.c -Wl,-Map,file_name.map

通过ld参数-Map:

ld -Map file_name.map helloworld.o -o helloworld

3 有啥用?

做出可执行文件下载到机器上,你如何知道程序段或数据段会不会太大,会不会超过ROM或RAM的size?你如何知道Link脚本有没有写错,每个程序区段都确实寻址到符合机器的存储器设定?当然你可以下载进机器运行就知道了吗?但是认为负责整合的工程师一定要检查下map文件,有些问题只会造成系统的不稳定,而不会马上死机,这种问题最麻烦。

4 例子

写了一段简单的代码,测试一下map文件:

#include

#include

#include

int g_var1;

char *g_var2 = "hello world !";

int test_func(int arg) {

static char *s_var1 = NULL;

static int s_var2 = 255;

int var = arg * 10;

return var;

}

static void *pthread_proc(void *arg)

{

/* 线程pthread开始运行 */

printf("pthread start!\n");

/* 令主线程继续执行 */

sleep(2);

return NULL;

}

int main(int argc, char **argv) {

pthread_t tidp;

g_var1 = 100;

printf("%s\n", g_var2);

printf("main=%016llx, func=%016llx, var=%016llx\n", (long long)main, (long long)test_func, (long long)g_var2);

test_func(g_var1);

pthread_create(&tidp, NULL, pthread_proc, NULL);

pthread_join(tidp, NULL);

printf("exit !\n");

return 0;

}

程序基本包含代码的所有关键点:全局变量、静态变量、局部变量、函数、链接库。执行编译命令gcc -o helloworld helloworld.c -Wl,-Map,helloworld.map -lpthread,生成map文件和可执行文件。

程序执行结果:

$ ./helloworld

hello world !

main=000055c70b2dd86e, func=000055c70b2dd82a, var=000055c70b2dd9c8

pthread start!

exit !

map文件片段1:

按需库被包含以满足文件 (符号) 引用

libpthread.so.0 /tmp/ccnK3bV5.o (pthread_create@@GLIBC_2.2.5)

libc.so.6 /tmp/ccnK3bV5.o (puts@@GLIBC_2.2.5)

分配公共符号

公共符号 大小 文件

g_var1 0x4 /tmp/ccnK3bV5.o

可以看到链接库(libpthread)的引用符号(pthread_create函数)、全局变量(g_var1)。

注意:静态变量和静态函数不会出现在map文件中!

map文件片段2:

.text 0x0000000000000700 0x2b /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o

0x0000000000000700 _start

.text 0x000000000000072b 0x0 /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o

*fill* 0x000000000000072b 0x5

.text 0x0000000000000730 0xda /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o

.text 0x000000000000080a 0xfc /tmp/ccnK3bV5.o

0x000000000000080a test_func

0x000000000000084e main

可以看到函数test_func和main的信息,其地址与程序运行时打印出的地址有必然联系(最后一个字节相同)。理论上打印的地址和map中的地址(物理地址)应该是一样的,但由于Linux系统的虚拟地址机制,打印出来的是虚拟地址,但是地址排列是一致的,所以最后一个字节相同。

map文件片段3:

.bss 0x0000000000201028 0x18

*(.dynbss)

.dynbss 0x0000000000201028 0x0 /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o

*(.bss .bss.* .gnu.linkonce.b.*)

.bss 0x0000000000201028 0x0 /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o

.bss 0x0000000000201028 0x0 /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o

.bss 0x0000000000201028 0x1 /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o

*fill* 0x0000000000201029 0x7

.bss 0x0000000000201030 0x8 /tmp/ccnK3bV5.o

.bss 0x0000000000201038 0x0 /usr/lib/x86_64-linux-gnu/libc_nonshared.a(elf-init.oS)

.bss 0x0000000000201038 0x0 /usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o

.bss 0x0000000000201038 0x0 /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o

*(COMMON)

COMMON 0x0000000000201038 0x4 /tmp/ccnK3bV5.o

0x0000000000201038 g_var1

由于g_var1是未初始化的全局变量,应该被分配在bss段,从map文件也可以验证这一点。

map文件片段4:

.data 0x0000000000201010 0x14 /tmp/ccnK3bV5.o

0x0000000000201010 g_var2

.rodata 0x00000000000009c8 0x50 /tmp/ccPjpRD1.o

同理,g_var2是初始化的全局变量,分配在数据段(.data)。g_var2的初始化数据“hello world !”放在只读数据段(.rodata)。

以上~~~