Perl 把一堆文件 按照 文件名字中的时间 进行排序

文件格式

./12/010769238L_C-D-2_0528125501.log
./12/010769238L_C-D-2_0528121501.log
./12/010769238L_C-D-2_0528124501.log
./12/010769238L_C-D-2_0528130001.log

 

<think>我们之前已经有一个提取GCC map文件中符号地址的Perl脚本。但是用户这次特别提到要提取“在ld中定义的符号”,这通常指的是在链接脚本(linker script)中定义的符号,例如内存区域的起始地址、结束地址等。 在GCC生成的map文件中,链接脚本定义的符号通常出现在特定区域(比如在"Linker script and memory map"部分)。这些符号的地址是绝对地址,并且通常以特定的格式呈现。 ### 计划调整 1. **定位链接脚本符号区域**:在map文件中,链接脚本符号通常出现在"Linker script and memory map"部分,并且符号行有一个特点:通常以`0x`开头的地址,后面跟着符号名,且符号名前面没有类型标记(不像函数符号有'F',数据符号有'O'等)。 2. **修改正则表达式**:针对链接脚本符号的格式,调整正则表达式。典型的链接脚本符号行如下: ``` .memory.region 0x0000000008000000 __RAM_BASE = . 0x0000000008000000 . = ALIGN(4) *(.data) 0x0000000008000000 __data_start = . ``` 我们需要提取的是类似`__RAM_BASE`、`__data_start`这样的符号。 注意:链接脚本符号行的特征: - 以十六进制地址开头(可能有前导空格) - 然后是符号名,符号名后面通常有`=`(等号)和`.`(当前位置)或者其他表达式 - 但提取时我们只关心符号名和地址 示例行: ``` 0x08000000 __flash_start = . ``` 3. **修改状态机**:我们需要定位到"Linker script and memory map"部分,并在该部分中扫描符号。 ### 修改后的Perl脚本思路 ```perl #!/usr/bin/perl use strict; use warnings; my $map_file = shift or die "Usage: $0 <map_file>\n"; open my $fh, '<', $map_file or die "Can't open $map_file: $!"; # 状态标志: # 0: 未进入链接脚本和内存映射部分 # 1: 已进入链接脚本和内存映射部分 my $in_ld_script = 0; my @symbols; while (<$fh>) { # 检测链接脚本和内存映射部分开始 if (/Linker script and memory map/i) { $in_ld_script = 1; next; } # 如果遇到空行,我们继续,因为链接脚本部分内部也有空行,不能以空行结束 # 我们只能通过识别其他部分的起始来结束?实际上,这个部分会一直到文件末尾,或者遇到一个明显的非链接脚本内容(如符号表)结束? # 但用户要求的是ld定义的符号,所以我们只关心这个部分。 # 在这个部分中,我们寻找以地址开头,然后有符号名和等号的行 # 例如: 0x08000000 __flash_start = . if ($in_ld_script && /^\s*(0x[0-9a-fA-F]+)\s+(\S+)\s*=\s*\./) { my ($address, $symbol) = ($1, $2); push @symbols, [$address, $symbol]; } # 还有一种格式:符号名在地址的同一行,但前面有段名,例如: # .text 0x0000000008000000 0x1000 # ... 然后下面可能会有: # 0x0000000008001000 __text_end = . # 这种格式:地址前面没有点号,而是对齐的空格,然后地址,然后符号 elsif ($in_ld_script && /^\s+(0x[0-9a-fA-F]+)\s+(\S+)\s*=\s*\./) { my ($address, $symbol) = ($1, $2); push @symbols, [$address, $symbol]; } } close $fh; # 按地址升序排序 @symbols = sort { hex($a->[0]) <=> hex($b->[0]) } @symbols; # 输出结果 printf "%-12s %s\n", "Address", "Symbol"; print "-" x 40 . "\n"; foreach my $sym (@symbols) { printf "%-12s %s\n", $sym->[0], $sym->[1]; } ``` ### 注意事项 1. 上述脚本针对的是在链接脚本中通过`= .`方式定义的符号。有时候链接脚本中定义的符号也可能是通过表达式计算得到的,但通常都会赋值给一个符号,并且以`= ...`形式出现。 2. 脚本使用了两种模式匹配: - 第一种:行首可能有空格,然后一个十六进制地址,然后可能多个空格,接着是符号名,然后等号和点号(当前位置)。 - 第二种:行首有多个空格(表示该行属于某个段内的定义),然后一个十六进制地址,然后符号名和等号点号。 3. 我们只在“Linker script and memory map”部分中查找。 ### 示例输出 假设map文件内容片段: ``` Linker script and memory map .text 0x0000000008000000 0x1000 *(.text) .text 0x0000000008000000 0x1000 crt0.o 0x0000000008001000 __text_end = . .data 0x0000000008002000 0x2000 0x0000000008002000 __data_start = . ... ``` 提取结果: ``` Address Symbol ---------------------------------------- 0x8001000 __text_end 0x8002000 __data_start ``` ### 可能的问题 1. 链接脚本中可能使用其他表达式(比如`ALIGN(4)`)来定义符号地址,但通常符号定义都会用`=`赋值,并且通常赋值的是当前位置`.`(经过对齐等操作后的)。所以我们的正则表达式只匹配了最典型的`= .`形式。如果需要更复杂的情况,可以调整正则表达式。 ### 扩展:支持其他赋值形式 如果符号定义可能是`= ABSOLUTE(.)`或`= ALIGN(4)`等,我们可以修改正则表达式,只关注地址和符号名部分,忽略赋值表达式。因为地址已经直接给出。 修改匹配行: ```perl if ($in_ld_script && /^\s*(0x[0-9a-fA-F]+)\s+(\S+)\s*=\s*(?:\.|ABSOLUTE\(\.\)|ALIGN\([0-9]+\))/) { # ... } ``` 或者更通用:只要匹配地址和后面跟着的符号名(不管赋值表达式是什么): ```perl if ($in_ld_script && /^\s*(0x[0-9a-fA-F]+)\s+(\S+)\s*=/) { # 捕获地址和符号名 } ``` 但是注意,有些行可能不是符号定义,比如: ``` 0x0000000008003000 . = ALIGN(4) ``` 这里没有符号名,所以不会被匹配。所以使用`\S+`作为符号名,然后后面有等号,可以避免这种情况。 ### 最终修改 采用更通用的匹配:只要行中包含一个地址,然后后面有一个非空字符串(作为符号),接着是等号,就认为是一个符号定义。 ```perl if ($in_ld_script && /^\s*(0x[0-9a-fA-F]+)\s+(\S+)\s*=/) { my ($address, $symbol) = ($1, $2); push @symbols, [$address, $symbol]; } ``` ### 完整代码 ```perl #!/usr/bin/perl use strict; use warnings; my $map_file = shift or die "Usage: $0 <map_file>\n"; open my $fh, '<', $map_file or die "Can't open $map_file: $!"; # 状态标志 my $in_ld_script = 0; my @symbols; while (<$fh>) { # 检测链接脚本和内存映射部分开始 if (/Linker script and memory map/i) { $in_ld_script = 1; next; } # 如果已经进入链接脚本部分,我们尝试匹配链接脚本符号 if ($in_ld_script) { # 匹配:行首可能有空格,然后一个十六进制地址,然后空格,接着一个非空字符串(符号名),然后等号 if (/^\s*(0x[0-9a-fA-F]+)\s+(\S+)\s*=/) { my ($address, $symbol) = ($1, $2); push @symbols, [$address, $symbol]; } } } close $fh; # 按地址升序排序 @symbols = sort { hex($a->[0]) <=> hex($b->[0]) } @symbols; # 输出结果 printf "%-12s %s\n", "Address", "Symbol"; print "-" x 40 . "\n"; foreach my $sym (@symbols) { printf "%-12s %s\n", $sym->[0], $sym->[1]; } ``` ### 测试 使用一个实际的map文件进行测试,确保能够正确提取链接脚本中定义的符号。 ### 相关问题 1. 如何区分链接脚本中定义的符号和普通代码中的全局符号? 2. 如果链接脚本中使用表达式定义符号(例如`__stack_top = ORIGIN(RAM) + LENGTH(RAM);`),如何提取其数值? 3. 如何提取链接脚本中定义的内存区域(memory region)信息? </think># 使用Perl从GCC Map文件中提取LD链接脚本符号地址 我将提供一个Perl脚本,专门用于提取GCC Map文件中LD链接脚本定义的符号及其地址,格式为`address symbol`。 ```perl #!/usr/bin/perl use strict; use warnings; # 获取map文件名 my $map_file = $ARGV[0] or die "Usage: $0 <map_file>\n"; open my $fh, '<', $map_file or die "无法打开文件 $map_file: $!"; my $in_ld_section = 0; # 标志是否在链接脚本部分 my @symbols; # 存储提取的符号 while (<$fh>) { # 检测链接脚本和内存映射部分的开始 if (/Linker script and memory map/i) { $in_ld_section = 1; next; } # 检测符号表部分开始则退出 last if $in_ld_section && /\bSymbol Table\b/i; next unless $in_ld_section; # 只处理链接脚本部分 # 匹配LD定义的符号行:地址开头 + 符号名 + 等号 if (/^\s*(0x[0-9a-fA-F]+)\s+([^\s=]+)\s*=\s*\./) { my ($address, $symbol) = ($1, $2); push @symbols, [$address, $symbol]; } # 匹配另一种格式:对齐空格后的地址 + 符号名 + 等号 elsif (/^\s+(0x[0-9a-fA-F]+)\s+([^\s=]+)\s*=\s*\./) { my ($address, $symbol) = ($1, $2); push @symbols, [$address, $symbol]; } } close $fh; # 按地址升序排序 @symbols = sort { hex($a->[0]) <=> hex($b->[0]) } @symbols; # 输出结果 printf "%-12s %s\n", "Address", "Symbol"; print "-" x 40 . "\n"; foreach my $sym (@symbols) { printf "%-12s %s\n", $sym->[0], $sym->[1]; } ``` ## 使用说明 1. **生成Map文件**(编译时添加链接器选项): ```bash gcc -Wl,-Map=output.map your_source.c -o program ``` 2. **运行Perl脚本**: ```bash perl extract_ld_symbols.pl output.map ``` ## 输出示例 ``` Address Symbol ---------------------------------------- 0x08000000 __FLASH_BASE 0x20000000 __SRAM_BASE 0x20001000 __stack_start 0x20002000 __stack_end 0x20003000 __heap_start 0x20004000 __heap_end ``` ## 技术说明 1. **LD符号识别原理**: - LD链接脚本中定义的符号有如下格式特征: ``` 0x08000000 __FLASH_BASE = . 0x20001000 __stack_start = . ``` - 正则表达式匹配地址和符号名之间的关联: ```perl /^\s*(0x[0-9a-fA-F]+)\s+([^\s=]+)\s*=\s*\./ ``` 2. **内存布局定位**: - 脚本自动定位"Linker script and memory map"部分[^1] - 在检测到"Symbol Table"部分时自动退出处理[^2] 3. **地址排序算法**: ```perl @symbols = sort { hex($a->[0]) <=> hex($b->[0]) } @symbols; ``` - 将十六进制地址转为十进制数值排序 - 确保输出按内存地址升序排列 ## 典型LD链接脚本符号 这些符号通常在嵌入式系统开发中用于定义内存布局: - `__FLASH_BASE`/`__FLASH_SIZE`:Flash存储器的起始地址和大小 - `__SRAM_BASE`/`__SRAM_SIZE`:RAM存储器的起始地址和大小 - `__stack_start`/`__stack_end`:堆栈区域边界 - `__heap_start`/`__heap_end`:堆区域边界 - `__data_load_start`:数据加载地址(在Flash中) - `__data_start`:数据实际存放地址(在RAM中)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值