单片机list文件解析
单片机list文件解析.
单片机开发中 lst 文件 在调试中有很高的参考价值, 从list文件总基本可用看出所有的目标文件信息. 看出我们汇编文件在内存中的分布及执行过程. 进而发现一些隐藏的BUG.
st 文件实际是使用 objdump 反汇编 elf 文件得到的输出文件,它拥有比 map 文件更详细的信息。如果你的程序中加入了调试信息,那么你可以在 lst 中看到每一条指令的地址。借助 lst 文件,同时通过查看栈帧结构(可以通过查看相应的手册来确定栈帧的组成),通过在 lst 文件中查找 lr 的地址所在的位置,你就能立刻定位到问题。
以CH582 的工程配置为例, 项目->属性->C/C++构建->设置->工具设置->GNU RISC-V Create Flash Image->General,你可以在这个选项中指定更详细的信息。
文件总含有丰富的信息. listing file中出现的先后顺序加以说明:
1.页头段(Page Header)
每个lst文件都有一个包含了编译器版本号、源文件名称、日期、时间、页号的头部。
示例:
EC_BLE_SOC.elf: file format elf32-littleriscv
architecture: riscv:rv32, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00000000
还包含内部所有代码块的, 头地址.
Program Header:
LOAD off 0x00001000 vaddr 0x00000000 paddr 0x00000000 align 2**12
filesz 0x00000004 memsz 0x00000004 flags r-x
LOAD off 0x00002000 vaddr 0x20000000 paddr 0x00000004 align 2**12
filesz 0x0000287c memsz 0x0000287c flags r-x
LOAD off 0x00004880 vaddr 0x00002880 paddr 0x00002880 align 2**12
filesz 0x000291bc memsz 0x000291bc flags r-x
LOAD off 0x0002e87c vaddr 0x2000287c paddr 0x0002ba3c align 2**12
filesz 0x000005ac memsz 0x00002f20 flags rw-
符号列表段(Sections table)
符号表段含有,所有符号的入口地址及大小,符号列表段包含了源程序文件中定义的变量信息,比如变量名称、类别(SFR、structure、typedef、static、public、auto、extern)、存储空间、数据类型、偏移量、占用 字节数等。SYMBOLS 指令控制LST文件产生符号列表段。
所有变量及函数的入口都在这里
示例1.段类型
Sections:
Idx Name Size VMA LMA File off Algn Flags
0 .init 00000004 00000000 00000000 00001000 2**1 CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .highcodelalign 00000000 00000004 00000004 0002ee28 2**0 CONTENTS
2 .highcode 0000287c 20000000 00000004 00002000 2**1 CONTENTS, ALLOC, LOAD, READONLY, CODE
3 .text 000291bc 00002880 00002880 00004880 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE
4 .fini 00000000 0002ba3c 0002ba3c 0002ee28 2**0 CONTENTS, ALLOC, LOAD, CODE
5 .dalign 00000000 2000287c 2000287c 0002ee28 2**0 CONTENTS
6 .dlalign 00000000 0002ba3c 0002ba3c 0002ee28 2**0 CONTENTS
7 .data 000005ac 2000287c 0002ba3c 0002e87c 2**2 CONTENTS, ALLOC, LOAD, DATA
8 .bss 00002974 20002e28 0002bfe8 0002ee28 2**3 ALLOC
9 .stack 00000000 20008000 20008000 0002ee28 2**0 CONTENTS
10 .debug_info 000399df 00000000 00000000 0002ee28 2**0 CONTENTS, READONLY, DEBUGGING
11 .debug_abbrev 00006f9c 00000000 00000000 00068807 2**0 CONTENTS, READONLY, DEBUGGING
12 .debug_loc 0000a5a1 00000000 00000000 0006f7a3 2**0 CONTENTS, READONLY, DEBUGGING
13 .debug_aranges 00000b40 00000000 00000000 00079d48 2**3 CONTENTS, READONLY, DEBUGGING
14 .debug_ranges 00001f40 00000000 00000000 0007a888 2**3 CONTENTS, READONLY, DEBUGGING
15 .debug_line 0001c557 00000000 00000000 0007c7c8 2**0 CONTENTS, READONLY, DEBUGGING
16 .debug_str 00006518 00000000 00000000 00098d1f 2**0 CONTENTS, READONLY, DEBUGGING
17 .comment 00000033 00000000 00000000 0009f237 2**0 CONTENTS, READONLY
18 .debug_frame 00002b40 00000000 00000000 0009f26c 2**2 CONTENTS, READONLY, DEBUGGING
19 .stab 00000084 00000000 00000000 000a1dac 2**2 CONTENTS, READONLY, DEBUGGING
20 .stabstr 00000117 00000000 00000000 000a1e30 2**0 CONTENTS, READONLY, DEBUGGING
示例2.程序及变量符号表
SYMBOL TABLE:
00000000 l d .init 00000000 .init
00000004 l d .highcodelalign 00000000 .highcodelalign
20000000 l d .highcode 00000000 .highcode
00002880 l d .text 00000000 .text
0002ba3c l d .fini 00000000 .fini
2000287c l d .dalign 00000000 .dalign
0002ba3c l d .dlalign 00000000 .dlalign
2000287c l d .data 00000000 .data
20002e28 l d .bss 00000000 .bss
20008000 l d .stack 00000000 .stack
00000000 l df *ABS* 00000000 core_riscv.c
00000000 l df *ABS* 00000000 CH58x_adc.c
00000000 l df *ABS* 00000000 CH58x_clk.c
00000000 l df *ABS* 00000000 CH58x_gpio.c
00000000 l df *ABS* 00000000 CH58x_uart0.c
00000000 l df *ABS* 00000000 CH58x_uart1.c
00000000 l df *ABS* 00000000 CH58x_uart2.c
00000000 l df *ABS* 00000000 CH58x_uart3.c
00000000 l df *ABS* 00000000 Command.c
20002db8 l O .data 00000004 erase_seq
20002dbc l O .data 00000004 p.6140
00000000 l df *ABS* 00000000 bsp_Initial.c
00000000 l df *ABS* 00000000 bsp_adc.c
00000000 l df *ABS* 00000000 bsp_ctrl.c
20002dc0 l O .data 00000004 s.6140
00000000 l df *ABS* 00000000 bsp_key.c
20002e40 l O .bss 00000006 keyc
20002e48 l O .bss 00000006 keys
00000000 l df *ABS* 00000000 bsp_led.c
00000000 l df *ABS* 00000000 bsp_storer.c
20002de3 l O .data 00000001 buz_count
20002de4 l O .data 00000001 led_count
00000000 l df *ABS* 00000000 message.c
00000000 l df *ABS* 00000000 gap.c
0000aae2 l F .text 00000022 disconnectNext
20002ea4 l O .bss 00000006 gapCurrentRandomAddr
20002eb4 l O .bss 00000004 pfnCentralCBs
20002eb8 l O .bss 00000004 pfnCentralConnCBs
20002ebc l O .bss 00000004 pfnPeripheralCBs
20002ec0 l O .bss 00000004 pfnPeripheralConnCBs
20002ec4 l O .bss 00000008 slaveUpdateReq
20002ecc l O .bss 00000001 terminateAllTaskID
20002ecd l O .bss 00000001 terminateReason
00000000 l df *ABS* 00000000 gap_central.c
0000c458 l F .text 0000003a gapSendScanEnable
源码段(Source Code)及汇编代码段(Assembly Listing)
源码段展示了每行源程序及其行号。
COND指令,可以控制条件编译代码(#if 作用范围内的代码)被源码段列出,NOCOND指令,可以控制条件编译代码不被源码段列出。
用户可以使用 LISTINCLUDE 指令,让源码段显示 #include 所包含的文件内容,默认情况下,#include包含的文件内容不被显示。
汇编代码段包含了由C语言程序编译产生的汇编代码,CODE 指令可以控制LST文件产生汇编代码段。
两者信息交错在一起, 可以对照看一下源码及编译好的目标汇编代码. 可以看出编译的情况.
源码及汇编代码段示例:启动代码
Disassembly of section .init:
00000000 <_sinit>:
.section .init,"ax",@progbits
.global _start
.align 1
_start:
j handle_reset
0: 0810206f j 2880 <handle_reset>
Disassembly of section .highcode:
20000000 <_highcode_vma_start>:
...
20000008: 0090 addi a2,sp,64
2000000a: 2000 fld fs0,0(s0)
2000000c: 0938 addi a4,sp,152
2000000e: 2000 fld fs0,0(s0)
20000010: bda9 j 1ffffe6a <_data_lma+0x1ffd442e>
20000012: f5f9 bnez a1,1fffffe0 <_data_lma+0x1ffd45a4>
20000014: 0094 addi a3,sp,64
20000016: 2000 fld fs0,0(s0)
....
主程序编译结果示例:
00006048 <main_task_init>:
//名称: sys_init
//功能: 系统主程序初始化
//参数: 无
//返回值: 无
void main_task_init(void)
{
6048: 1141 addi sp,sp,-16
SetSysClock( CLK_SOURCE_PLL_60MHz );
604a: 04800513 li a0,72
{
604e: c606 sw ra,12(sp)
SetSysClock( CLK_SOURCE_PLL_60MHz );
6050: 1fffa097 auipc ra,0x1fffa
6054: 490080e7 jalr 1168(ra) # 200004e0 <SetSysClock>
motor_io_Init();
6058: 9e3fe0ef jal ra,4a3a <motor_io_Init>
DelayMs(100);
605c: 06400513 li a0,100
6060: 1fffa097 auipc ra,0x1fffa
6064: 638080e7 jalr 1592(ra) # 20000698 <mDelaymS>
sys_para_init(); //初始化全局参数
6068: fcdfe0ef jal ra,5034 <sys_para_init>
sys_state_init();
606c: ed7fe0ef jal ra,4f42 <sys_state_init>
sys_monitor_init();//启动看门狗监视
6070: 3e5030ef jal ra,9c54 <sys_monitor_init>
......
00006084 <main>:
* Input : None
* Output : None
* Return : None
*******************************************************************************/
int main(void)
{
6084: 1141 addi sp,sp,-16
6086: c606 sw ra,12(sp)
main_task_init();
6088: 37c1 jal 6048 <main_task_init>
systask_Create();
608a: 355d jal 5f30 <systask_Create>
log("%s\r\n", VER_LIB);
608c: 1fffa097 auipc ra,0x1fffa
6090: 628080e7 jalr 1576(ra) # 200006b4 <Log_get_pbuff>
6094: 00025617 auipc a2,0x25
6098: 2bc60613 addi a2,a2,700 # 2b350 <VER_LIB>
609c: 00025597 auipc a1,0x25
60a0: f9058593 addi a1,a1,-112 # 2b02c <DeviceName+0x3ec>
60a4: 565220ef jal ra,28e08 <siprintf>
60a8: 1fffa097 auipc ra,0x1fffa
60ac: 676080e7 jalr 1654(ra) # 2000071e <Log_pbuff_out>
log("\r\n系统启动完成2\r\n");
60b0: 1fffa097 auipc ra,0x1fffa
60b4: 604080e7 jalr 1540(ra) # 200006b4 <Log_get_pbuff>
60b8: 4649 li a2,18
60ba: 00025597 auipc a1,0x25
60be: a5e58593 addi a1,a1,-1442 # 2ab18 <FW_VERSION+0x10>
60c2: 36b220ef jal ra,28c2c <memcpy>
60c6: 1fffa097 auipc ra,0x1fffa
60ca: 658080e7 jalr 1624(ra) # 2000071e <Log_pbuff_out>
while(1) {
TMOS_SystemProcess();
60ce: 1fffb097 auipc ra,0x1fffb
60d2: 17a080e7 jalr 378(ra) # 20001248 <TMOS_SystemProcess>
60d6: bfe5 j 60ce <main+0x4a>
......
子程序编译结果示例:
20000698 <mDelaymS>:
__HIGH_CODE
void mDelaymS(uint16_t t)
{
uint16_t i;
for(i = 0; i < t; i++)
20000698: cd09 beqz a0,200006b2 <mDelaymS+0x1a>
2000069a: 4701 li a4,0
2000069c: 6691 lui a3,0x4
2000069e: a9868793 addi a5,a3,-1384 # 3a98 <Log_uart1_txisr+0x5c>
__nop();
200006a2: 0001 nop
200006a4: 17fd addi a5,a5,-1
} while(--i);
200006a6: fff5 bnez a5,200006a2 <mDelaymS+0xa>
for(i = 0; i < t; i++)
200006a8: 0705 addi a4,a4,1
200006aa: 0742 slli a4,a4,0x10
200006ac: 8341 srli a4,a4,0x10
200006ae: fee518e3 bne a0,a4,2000069e <mDelaymS+0x6>
{
mDelayuS(1000);
}
}
200006b2: 8082 ret
参考list文件追踪 hardfalt 问题
从 .lst 文件总可以看到很多有用的信息, 特别是代码的编译结果和在内存中的分布. 结合调试PC信息能很容易追踪到棘手的 hardfalt 问题. 以及找出程序报错的地方
写一段错误代码:
//非对齐的地址取int数据
void hardfault_test(void) {
int a,*p;
while(1){
DelayMs(100);
for(int i=0;i<16;i++) {
p = (int *)(0x200+i);
a = *p;
printf("test:%X\r\n",a);
}
}
}
在hardfalt中断中打印出错点 PC值
hardfault
mepc:000025ca
mcause:00000004
mtval:00000201
编译出来的list结果:
//对非4字节对齐的地址取uint32_t
void hardfault_test(void) {
25a4: 1141 addi sp,sp,-16
25a6: c226 sw s1,4(sp)
25a8: c04a sw s2,0(sp)
25aa: c606 sw ra,12(sp)
25ac: c422 sw s0,8(sp)
DelayMs(100);
for(int i=0;i<16;i++) {
p = (int *)(0x200+i);
a = *p;
printf("test:%X\r\n",a);
25ae: 00002917 auipc s2,0x2
25b2: 91a90913 addi s2,s2,-1766 # 3ec8 <cmd_mm+0x78>
for(int i=0;i<16;i++) {
25b6: 21000493 li s1,528
DelayMs(100);
25ba: 06400513 li a0,100
25be: 1fffe097 auipc ra,0x1fffe
25c2: 0da080e7 jalr 218(ra) # 20000698 <mDelaymS>
25c6: 20000413 li s0,512
printf("test:%X\r\n",a);
25ca: 400c lw a1,0(s0)
25cc: 854a mv a0,s2
25ce: 0405 addi s0,s0,1
25d0: 2d01 jal 2be0 <iprintf>
for(int i=0;i<16;i++) {
25d2: fe941ce3 bne s0,s1,25ca <hardfault_test+0x26>
25d6: b7d5 j 25ba <hardfault_test+0x16>
很明显就在list文件里面,我们可以找到000025ca地址对应的行, 这一行是一条取值的指令, 从0x0201的地址尝试取出一个int. 很明显这个里不对齐的地址会产生了一个hardfault.