以前查看map,基本就是看一下Flash大小、和RAM总大小,或者是debug时确认下 函数编译地址。
最近心血来潮,想用C#做个上位机来解一下这个map文件,所以又回过头来关注下map文件具体格式和细节。
map文件的具体结构:
1、Section Cross References
段交叉引用
这部分具体描述了各个文件和模块之间的交叉引用关系。是程序编译和链接过程中的一个输出结果。查看这部分内容,可以知道一个文件的引用关系和被引用关系。
2、Removing Unused input sections from the image
移除镜像中未使用的输入部分
编译器会自动删除未使用的代码端,可用于查看多余代码量。
3、Image Symbol Table
镜像符号表
它是详细描述程序中各个段在存储器中的地址、类型、大小等信息,对于理解内存布局和程序结构有很大帮助。
4、Memory Map of the image
映像内存映射
它描述了程序映像(即编译后的可执行文件或固件)在内存中的布局。这个映射详细说明了程序的各个部分(如代码段、数据段、堆栈、堆等)在物理或虚拟内存中的位置、大小和属性。
5、Image component sizes
镜像分配大小
是指STM32等嵌入式系统编译后生成的映像文件(Image)中各个组成部分所占用的存储空间大小。这些信息对于理解程序的内存使用情况、优化代码和内存布局至关重要。
具体各个模块的格式解析:
Section Cross References
实例:
system_stm32f4xx.o(i.SystemCoreClockUpdate) refers to system_stm32f4xx.o(.data) for SystemCoreClock
system_stm32f4xx.c的函数SystemCoreClockUpdate 引用了 system_stm32f4xx.c 的函数SystemCoreClock
startup_stm32f429xx.o(RESET) refers to stm32f4xx_it.o(i.HardFault_Handler) for HardFault_Handler
startup_stm32f429xx.c(RESET段) 引用了 stm32f4xx_it.c 的函数HardFault_Handler
总结: XX.c 引用了 YY.c 的 ZZ (函数或全局变量)
衍生:
1)基于XX,可以筛选一个c文件引用了哪些函数或变量
2)基于YY,可以筛选一个c文件被哪些文件引用了
3)基于ZZ,可以筛选一个函数或变量被哪些文件引用了
说明:
i.FF 通常表示一个函数
(.data) 表示内存中的data段 初始化了的全局变量或静态变量
类似的还有 (.text)/(.code) 代码段 (.bss) 未初始化的全局变量或静态变量 (.heap) (.stack)
其中, (RESET)是一个特殊段 它是STM32控制器复位向量的地址
Removing Unused input sections from the image
实例:
Removing system_stm32f4xx.o(.rev16_text), (4 bytes).
移除了system_stm32f4xx.c的rev16_text端的代码 共4字节
Removing stm32f4xx_hal_msp.o(i.HAL_ADC_MspDeInit), (100 bytes)
移除了stm32f4xx_hal_msp.c的HAL_ADC_MspDeInit函数 共100字节
总结: 移除了XX.c的YY代码 共Z个字节
衍生:
1)基于XX和Z,可以筛选一个c文件的多于代码量
2)基于XX和YY,可以明确哪些函数未用到
Image Symbol Table
实例:
Symbol Name Value Ov Type Size Object(Section)
../Core/Src/main.c 0x00000000 Number 0 main.o ABSOLUTE
../USB_DEVICE/App/usb_device.c 0x00000000 Number 0 usb_device.o ABSOLUTE
..\VciModle\App\VciModleApp.c 0x00000000 Number 0 vcimodleapp.o ABSOLUTE
i.ArkCan1_Task 0x08011e20 Section 0 arkcantask.o(i.ArkCan1_Task)
i.ArkCan2_Task 0x08011f60 Section 0 arkcantask.o(i.ArkCan2_Task)
i.ArkCanBaudRangeCheck 0x08012068 Section 0 arkcmdintelligent.o(i.ArkCanBaudRangeCheck)
ArkCanBaudRangeCheck 0x08012069 Thumb Code 84 arkcmdintelligent.o(i.ArkCanBaudRangeCheck)
i.ArkCanCmdSendFinish 0x080120c8 Section 0 arkcantask.o(i.ArkCanCmdSendFinish)
依次是符号名称、符号的地址、符号类型、符号大小和地址空间
总结:可以找到单个函数的地址和代码量大小
Memory Map of the image
实例:
Image Entry point : 0x080101ad
Load Region LR_IROM1 (Base: 0x08010000, Size: 0x0006c0c8, Max: 0x00100000, ABSOLUTE, COMPRESSED[0x0006bc98])
Execution Region ER_IROM1 (Exec base: 0x08010000, Load base: 0x08010000, Size: 0x0006b548, Max: 0x00100000, ABSOLUTE)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x0801bf84 0x0801bf84 0x00000038 Code RO 2322 i.CRC32_Add vcicommonfun.o
0x0801bfbc 0x0801bfbc 0x0000000c Code RO 2323 i.CRC32_Get vcicommonfun.o
0x0801bfc8 0x0801bfc8 0x00000010 Code RO 2324 i.CRC32_Init vcicommonfun.o
依次是 执行地址、加载地址、大小、类型、属性、索引 名称和所属的对象
类型见段交叉引用中的“说明”、属性表示读写属性、名称即函数名
总结:可以找到单个函数的地址和代码量大小,以及属性等信息
Image component sizes
实例:
Code (inc. data) RO Data RW Data ZI Data Debug Object Name
3870 272 180 2 264 18242 arkcantask.o
总结:用于统计整个文件的RAM和ROM占用量
说明:可基于上位机工具,计算每个文件的RAM和ROM占用量,转成表格文件进行进一步分析
C# 解析Image component sizes
public static string FileAnalyzing(string path)
{
StreamReader sr = new StreamReader(path);
string line, tempStr, retStr = null, content = null;
int line_status = 0, index = 0, temp;
line = sr.ReadLine();
while (line != null)
{
if (line.Contains("Component"))
{
retStr += "编译器版本" + line.Substring(10) + "\r\n";
}
else if (line.Contains("Image component sizes"))
{
retStr += "\r\n内存分配表\r\n==============================================================================\r\n";
line_status = 1;
}
else if (line.Contains("----------------------------------------------------------------------"))
{
line_status = 0;
}
else if (line.Contains("Total ROM Size"))
{
retStr += line.Replace("Total ROM Size (Code + RO Data + RW Data)", "Flash:") + "\r\n";
}
else if (line.Contains("Total RW Size"))
{
retStr += "==============================================================================\r\n";
retStr += line.Replace("Total RW Size (RW Data + ZI Data) ", "ROM: ") + "\r\n";
}
if (line_status == 1)
{
if (line.Contains("Code (inc. data)"))
{
retStr += "Index RAM ROM code data RO RW ZI Debug Object Name\r\n";
content += "Index RAM ROM code data RO RW ZI Debug ObjectName\r\n";
line_status = 2;
index = 0;
}
}
else if (line_status == 2)
{
string[] parts = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 7)
{
/*
3870 Code
272 (inc. data)
180 RO Data
2 RW Data
264 ZI Data
18242 Debug
arkcantask.o Object Name
Total ROM Size (Code + RO Data + RW Data)
Total RW Size (RW Data + ZI Data)
*/
index++;
tempStr = index.ToString().PadLeft(4);//索引
temp = Convert.ToInt32(parts[0]) + Convert.ToInt32(parts[2]) + Convert.ToInt32(parts[3]);
tempStr += temp.ToString().PadLeft(6);//RAM
temp = Convert.ToInt32(parts[3]) + Convert.ToInt32(parts[4]);
tempStr += temp.ToString().PadLeft(7);//ROM
tempStr += parts[0].PadLeft(6);
tempStr += parts[1].PadLeft(5);
tempStr += parts[2].PadLeft(5);
tempStr += parts[3].PadLeft(5);
tempStr += parts[4].PadLeft(7);
tempStr += parts[5].PadLeft(7) + " ";
tempStr += parts[6].Replace(".o", ".c") + "\r\n";
retStr += tempStr;
tempStr = index.ToString() + " ";//索引
temp = Convert.ToInt32(parts[0]) + Convert.ToInt32(parts[2]) + Convert.ToInt32(parts[3]);
tempStr += temp.ToString() + " ";//RAM
temp = Convert.ToInt32(parts[3]) + Convert.ToInt32(parts[4]);
tempStr += temp.ToString() + " ";//ROM
tempStr += parts[0] + " ";
tempStr += parts[1] + " ";
tempStr += parts[2] + " ";
tempStr += parts[3] + " ";
tempStr += parts[4] + " ";
tempStr += parts[5] + " ";
tempStr += parts[6].Replace(".o", ".c") + "\r\n";
content += tempStr;
}
}
line = sr.ReadLine();
}
sr.Close();
retStr += "\r\nText文本\r\n==============================================================================\r\n" + content;
return retStr;
}
主要思路就是,逐行解析文本文件,以空格作为分隔符得到各个元素,再进行展示。
同时再打印一份精简版的文本,可以用于将文本转为execl文件,进行图表分析。