网志

在ELF和DWARF的帮助下分析链接器映射文件

戈文德·穆昆丹2015年12月27日20条评论

在编写固件时,总会出现需要检查工作量消耗的资源的时间-也许是因为RAM或Flash用光了,或者您想优化某些东西。链接器生成的映射文件是有助于资源分析的有用工具。我想对以交互方式生成的数据进行过滤和排序,所以我编写了一个C#WinForms应用程序,该应用程序从地图中读取数据并将其显示在列表视图中(使用真棒 ObjectListView 控制)。除了“模块”或基于文件的资源消耗外,它还向您显示一些 信息不存在 在地图文件中,就像项目中使用的所有全局和静态符号(函数和变量)一样。该应用程序的屏幕截图如下所示。

可以从以下位置下载代码 的GitHub

本文以PDF格式提供,便于打印

该应用程序是在我为固件编写固件时开发的 FT900 微控制器,这是FTDI推出的新型32位微控制器。但是,由于FT32工具链基于GCC,因此可以轻松配置/更新应用程序代码,使其与其他基于GCC + 联用的工具链一起工作(我已经在Microchip的XC16工具链上进行过尝试)。

在本文中,我将首先讨论在映射文件中公开(而不公开)的数据,然后是程序本身的描述。

基本程序的内存映射

一个简单的嵌入式系统通常具有如下所示的内存映射(忽略外围内存)。在最高级别,程序可以分为几个部分-代码和程序所处理的数据。嵌入式系统中的代码进入闪存,如果数据是可变的,则数据进入RAM;如果是只读的,则数据至闪存。程序使用的数据可以分为静态分配的全局数据区域-初始化的DATA段和零初始化的BSS段,以及运行时分配的内存-堆栈和堆。

   ,'''''''''''''''`.0x00000000
   |                |
   |     DATA       |
   |________________|
   |                |
   |     BSS        |
   |________________|
   |       |        |
   |       |        |
   |       |        |
   |       V        |
   |     HEAP       |
   |                |
   |                |
   |                |
   |     STACK      |
   |       ^        |
   |       |        |
   |       |        |
   |       |        |RAM_MAX_ADDRESS
   '`''''''''''''''''

这就是我们执行程序时存储器的外观。但是,在大爆炸发生之前的一段时间里,程序完全驻留在闪存中,而RAM只是一堆随机位。当程序运行全局数据时,程序访问时碰巧具有正确的值,并且存储器映射看起来像上面那样井井有条,怎么会这样呢?答案是程序可执行文件包含引导程序代码,该引导程序代码在引用数据的任何代码执行之前将RAM初始化为程序正确的内存映射。在基于C的系统上,此引导代码称为ctr0(C运行时为零)。

这意味着程序使用的所有数据的初始值必须本身存储在程序可执行文件中,否则无法通过crt0初始化RAM。通常,此数据存储在程序中的所有代码之后。 bin / hex文件的典型映射如下所示

   ,'''''''''''''''''''''|FILE_START
   |_/Interrupt\Vectors\_|
   | \_/ \_/ \_/ \_/ \_/ |
   |_/REST OF/CODE \_/ \_|
   | \INSTRUCTIONS_/ \_/ |
   .......................
   |'    ':..:'    ':..:'|
   |. INITIAL:VALUES:'':.|
   |':OF VARIABLES:IN   '|
   |.:DATA SEGMENT:.    .|
   L_____________________|FILE_END

结果,当您声明一个初始化为某个非零值的全局变量时,不仅会消耗RAM,还会消耗Flash!

幸运的是,回到过去 当男人不刮胡子的时候 有人发现大多数全局变量通常都初始化为零,如果将所有这些零初始化的全局变量放在单独的RAM块中,只需指定起始地址+本节的长度,就可以节省一些闪存空间。因此,BSS诞生了,声称懒惰的程序员未初始化的所有全局变量以及专门初始化为零的全局变量。

执行期间和构建可执行文件时使用的此内存映射的详细信息在 链接描述文件 文件。 FT32链接程序脚本文件如下所示:

MEMORY
{
  flash     (rx)   : ORIGIN = 0, LENGTH = 150K
  ram       (rw!x) : ORIGIN = 0x800000, LENGTH = 32K
}
SECTIONS
{
  。文本 :
  {
    ./crt0.o(.text*)
    *(.text*)
    *(.strings)
    *(._pm*)
    *(.init)
    *(.fini)
     _etext = . ;
    . = ALIGN(4);
  }  > flash
  .data   : AT (ADDR (.text) + SIZEOF (.text))
  {
    . = ALIGN(4);
    __data = .;
    *(.data)
     *(.data*)
     *(.rodata)
    *(.rodata*)
     _edata = . ;
  }  > ram
  . = ALIGN(4);
  .bss  SIZEOF(.data) + ADDR(.data) :
  {
     _bss_start = . ;
    *(.bss)
    *(COMMON)
     _end = . ;
  }  > ram
   __data_load_start = LOADADDR(.data);
   __data_load_end = __data_load_start + SIZEOF(.data);

该文件输入到链接器,并允许链接器将每个目标文件中的文本/数据/ bss区域映射到实际的物理地址,并且还影响所有节的合并到最终的可执行文件中。有关更多信息,请参阅LD文档。 脚本的语法。对于我们的意图,我们可以注意到,名为 。文本, .strings等进入Flash中的.text段, 。数据, 数据,* bss等进入了ram。 .data段本身使用。 在() 指示。

要查看构建中正在使用的链接描述文件,请使用 -详细 LD的选项。

解释地图文件

可以通过传递地图文件来生成 --print-map> MyMapFile.txt 字符串到LD。除此之外 --cref 选项将打印交叉引用表,当您要跟踪对符号的引用时,这将非常有用。向下滚动地图文件,您会遇到一个名为 链接描述文件和内存映射。其中包含链接到最终映像的每个文件的内存贡献的细分。

为了说明分析,我创建了一个包含两个文件的简单项目。所有的解释将基于这个简单的项目。使用以下内容编译该项目

'Building file: ../main.c'
'Invoking: FT90x GCC Compiler'
ft32-elf-gcc -I"C:/Program Files (x86)/FTDI/FT90x Toolchain/Toolchain/hardware/include" -O0 -g -fvar-tracking -fvar-tracking-assignments -Wall -c -fmessage-length=0 -ffunction-sections -MMD -MP -MF"main.d" -MT"main.d" -o "main.o" "../main.c"
'Finished building: ../main.c'
' '
'Building file: ../test.c'
'Invoking: FT90x GCC Compiler'
ft32-elf-gcc -I"C:/Program Files (x86)/FTDI/FT90x Toolchain/Toolchain/hardware/include" -O0 -g -fvar-tracking -fvar-tracking-assignments -Wall -c -fmessage-length=0 -ffunction-sections -MMD -MP -MF"test.d" -MT"test.d" -o "test.o" "../test.c"
'Finished building: ../test.c'
' '
'Building target: MapParser.elf'
'Invoking: FT90x GCC Linker'
ft32-elf-gcc -L"C:/Program Files (x86)/FTDI/FT90x Toolchain/Toolchain/hardware/lib" -Wl,-gc-sections -Wl,--entry=_start -Wl,--print-map > Linker.map -Xlinker --cref -Xlinker -详细 -o "MapParser.elf"  ./main.o ./test.o   -lc -lstub -lft900
'Finished building target: MapParser.elf'

示例项目的文本区域如下所示:

。文本           0x00000000     0xf080
 *(.text*)
 。文本          0x00000000      0x310 /cygdrive/c/Program Files (x86)/FTDI/FT90x Toolchain/Toolchain/tools/bin/../lib/gcc/ft32-elf/5.0.0/crti-hw.o
                0x00000000                _start
                0x00000090                _exithook
                0x000000fc                _exit
                0x0000030c                nullvector
                0x00000310                __gxx_personality_sj0
 。文本.StaticFunc
                0x00000310       0x20 ./main.o
 。文本.GlobalFunc
                0x00000330       0x20 ./main.o
                0x00000330                全球Func
 。文本.main     0x00000350       0x44 ./main.o
                0x00000350                main
 。文本.StaticFunc
                0x00000394       0x38 ./test.o
 。文本.Test     0x000003cc       0x14 ./test.o
                0x000003cc                Test
                ....

我们可以看到.text段从地址0x00000000开始,大小为0xF080字节。 。文本段中的第一个模块是一个名为ctri-hw.o的文件,大小为0x310字节,并且在此文件中定义了4个全局函数。的 -功能部分 选项已确保每个函数都在单独的部分中编译,这允许链接器(通过 -Wl,-gc-sections),以便以后从最终可执行文件中删除所有未调用的功能。

类似的地图信息可用于.data和.bss段。然而 数据段和bss段中的静态符号丢失!

读取模块对应的行很简单,就可以使模块明智地分解内存。 .o 文件并提取大小。

交叉参考表

交叉引用表显示在内存映射下方,并向您显示项目中所有全局符号的列表。第一行表示在其中定义符号的文件,而后几行则表示使用该符号的所有位置。这对于跟踪使用了不应使用的内容的“恶意模块”非常有用。

Cross Reference Table
Symbol                                   File
BSSGlobal                                ./main.o
DataGlobal                               ./main.o
GlobalFunc                               ./main.o     ----> The above 3 symbols are not accessed outside main.o
Test                                     ./test.o     ----> The symbol "Test" is defined in test.o
                                         ./main.o     ----> The symbol "Test" is referenced in main.o

Hunting down statics with Some ELF Magic

映射文件中缺少静态符号,这对于认真地使用静态变量和函数来协助数据封装原理并允许编译器更好地进行优化的认真工作的程序员来说,是很烦人的。实际上,如果我们没有使用 -功能部分 选项静态函数在地图中也将丢失。幸运的是,有一些方法可以解决此限制。

对于大多数嵌入式系统,构建过程的最终输出是BIN或HEX文件,可以将其编程到微控制器闪存中。但是,在进入HEX文件之前,链接器实际上会生成另一个可执行文件,我们从中提取.text段以获取十六进制文件。此可执行文件有多种格式,最常见的是COFF和ELF。我们将考虑ELF,因为它是更灵活的格式。通常,许多嵌入式工具链会在同一输出目录中生成ELF和HEX文件。

假设您已经成功掌握了项目的ELF可执行文件,我们可以尝试通过使用来从其中获取更多信息 联用 程序,希望它们是您的工具链分发中的一部分。除了包含程序的所有段(在链接描述文件中定义)外,ELF映像还包含一个 符号表 其中列出了可执行文件中的所有全局(静态和非静态)符号。使用 READELF 在此输出中读出项目结果的符号表

$ C:\Program Files (x86)\FTDI\FT90x Toolchain\Toolchain\tools\bin> .\ft32-elf-readelf.exe -s "MapParserTest_FT32\Debug\MapParser.elf"
Symbol table '.symtab' contains 342 entries:
Num:    Value  Size Type    Bind   Vis      Ndx Name
 0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
 1: 00000000     0 部分 LOCAL  DEFAULT    1 
 2: 00800000     0 部分 LOCAL  DEFAULT    2 
 3: 00800000     0 部分 LOCAL  DEFAULT    3 
....
142: 00800a48    40 OBJECT  GLOBAL DEFAULT    3 __mprec_tinytens
143: 0000a0b4    84 FUNC    GLOBAL DEFAULT    1 _lseek_r
144: 0000d31c   204 FUNC    GLOBAL HIDDEN     1 __eqdf2
145: 0000030c     0 NOTYPE  GLOBAL DEFAULT    1 nullvector
146: 0000ba94     0 NOTYPE  GLOBAL HIDDEN     1 __prolog_$r28
147: 0000b828     0 NOTYPE  GLOBAL HIDDEN     1 __epilog24_$r19
148: 00000410    60 FUNC    GLOBAL DEFAULT    1 printf
...

现在我们也可以看到所有静态符号及其地址。但是我们仍然没有一种简单的方法来查看其大小和源文件名。原来BINUTILS有一个名为 NM 正是为此目的而编写的!运行带有选项的NM(按大小排序并指示行号)将产生此输出。

$ C:\Program Files (x86)\FTDI\FT90x Toolchain\Toolchain\tools\bin> .\ft32-elf-nm.exe --print-size --size-sort --line-numbers "MapParserTest_FT32\Debug\MapParser.elf" 
00800d20 00000004 B BSSGlobal
00800084 00000004 D DataGlobal
00800098 00000004 d FuncScopeStatic.2411
0080008c 00000004 d FuncScopeStatic.2413
00800090 00000004 d FuncScopeStatic.2417
00800ce4 00000004 b StaticBSSGlobal
00800ce8 00000004 b StaticBSSGlobal
00800094 00000004 d StaticDataGlobal
00800088 00000004 d StaticDataGlobal
....
00000330 00000020 T 全球Func  /cygdrive/c/Users/eGov/workspace/MapParser/Debug/../main.c:15
00000310 00000020 t StaticFunc  /cygdrive/c/Users/eGov/workspace/MapParser/Debug/../main.c:10
00000394 00000038 t StaticFunc  /cygdrive/c/Users/eGov/workspace/MapParser/Debug/../test.c:8

现在,我们有了所有符号的大小以及其中许多符号的源文件。不幸的是,NM没有列出静态变量的行号。因此,我们的旅程继续进入矮人之地。

认识DWARF

任何使用过调试器的人都知道,调试器对静态变量并不盲目,并且能够识别它们的源文件。因此,我们要做的就是调查调试器用来解决问题的信息。矮人是每个人都使用的通用调试信息标准。 DWARF调试信息本身存储在ELF文件中的单独“调试”部分中。我们可以使用以下方法提取调试信息 对象转储 或READELF。 (该工具使用readelf,因为xc16 objdump似乎无法提取调试信息)

矮人根据以下方面组织信息 编制单位 这是说文件的另一种方式。每个编译单元都有一个与其关联的信息树,其中包括该文件中定义的所有函数和变量(还有很多其他事情)。提取 dwarf_info 细分给我们:

$ .\ft32-elf-objdump.exe --dwarf=info path_to_elf > path_to_op_file
  Compilation Unit @ offset 0x0:
   Length:        0x1a5 (32-bit)
   Version:       4
   Abbrev Offset: 0x0
   Pointer Size:  4
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <c>   DW_AT_producer    : (indirect string, offset: 0x6d): GNU C11 5.0.0 20150310 (experimental) -g -O0 -fvar-tracking -fvar-tracking-assignments -fmessage-length=0 -ffunction-sections
    <10>   DW_AT_language    : 12   (ANSI C99)
    <11>   DW_AT_name        : (indirect string, offset: 0x2e): ../main.c
    <15>   DW_AT_comp_dir    : (indirect string, offset: 0x134): /cygdrive/c/Users/eGov/workspace/MapParser/Debug
    <19>   DW_AT_ranges      : 0x0
    <1d>   DW_AT_low_pc      : 0x0
    <21>   DW_AT_stmt_list   : 0x0
.....
 <1><234>: Abbrev Number: 6 (DW_TAG_subprogram)
    <235>   DW_AT_name        : (indirect string, offset: 0x4f): StaticFunc
    <239>   DW_AT_decl_file   : 1
    <23a>   DW_AT_decl_line   : 8
    <23b>   DW_AT_prototyped  : 1
    <23b>   DW_AT_low_pc      : 0x394
    <23f>   DW_AT_high_pc     : 0x38
    <243>   DW_AT_frame_base  : 1 byte block: 9c    (DW_OP_call_frame_cfa)
    <245>   DW_AT_GNU_all_call_sites: 1
    <245>   DW_AT_sibling     : <0x285>
....
 <1><147>: Abbrev Number: 7 (DW_TAG_variable)
    <148>   DW_AT_name        : (indirect string, offset: 0x113): StaticDataGlobal
    <14c>   DW_AT_decl_file   : 1
    <14d>   DW_AT_decl_line   : 6
    <14e>   DW_AT_type        : <0x64>
    <152>   DW_AT_location    : 5 byte block: 3 88 0 0 0    (DW_OP_addr: 88)
 <1><158>: Abbrev Number: 7 (DW_TAG_variable)
    <159>   DW_AT_name        : (indirect string, offset: 0xe): StaticBSSGlobal
    <15d>   DW_AT_decl_file   : 1
    <15e>   DW_AT_decl_line   : 7
    <15f>   DW_AT_type        : <0x64>
    <163>   DW_AT_location    : 5 byte block: 3 e4 c 0 0    (DW_OP_addr: ce4)
 <1><169>: Abbrev Number: 13 (DW_TAG_variable)
    <16a>   DW_AT_name        : (indirect string, offset: 0x119): DataGlobal
    <16e>   DW_AT_decl_file   : 1
    <16f>   DW_AT_decl_line   : 4
    <170>   DW_AT_type        : <0x64>
    <174>   DW_AT_external    : 1
    <174>   DW_AT_location    : 5 byte block: 3 84 0 0 0    (DW_OP_addr: 84)
 <1><17a>: Abbrev Number: 13 (DW_TAG_variable)
    <17b>   DW_AT_name        : (indirect string, offset: 0x14): BSSGlobal
    <17f>   DW_AT_decl_file   : 1
    <180>   DW_AT_decl_line   : 5
    <181>   DW_AT_type        : <0x64>
    <185>   DW_AT_external    : 1
    <185>   DW_AT_location    : 5 byte block: 3 20 d 0 0    (DW_OP_addr: d20)
.....

树中用标签标识的第一个元素 <0> 是恰好是 main.c. Subsequent elements related to main.c are identified by tags grater than 0. Observe that functions are identified with the tag DW_TAG_subprogram. DW_AT_low_pc indicates the 起始地址 of the function, this is the same as what's indicated by the NM output 起始地址. DW_AT_high_pc indicates the 字节大小 of the function. Similarly variables are identified by the tag DW_TAG_variableDW_OP_addr indicates the 起始地址 in RAM of the variable.

因此,假设我们有来自NM的符号列表,我们只需要在特定地址找到与每个符号相对应的编译单元。符号名称和(运行时)地址的组合唯一地定位了所有全局函数和变量,即使它们被声明为静态的并且在不同文件中具有多个定义。

请注意,要生成最大的调试信息,应使用 -g3 LD选项。额外的调试信息仅进入ELF文件,而不进入二进制/ HEX文件,因此,通过在构建中添加-g3选项,通常不会对微控制器资源造成任何不利影响。

实施细节

现在,我已经解释了如何获取数据,我将简要介绍MapViewer软件的详细信息,以便读者可以修改代码以适合其目标。

用户界面

用户界面主要由两个列表视图组成-一个列出可执行文件的文件/模块明智分割,另一个列出项目中的所有符号。过滤器可用于查看文件和/或符号子集的贡献。过滤一组文件会自动过滤该组中的符号。 。

设置视图用于设置地图/小精灵文件的路径,以及NM和OBJDUMP的BINUTILS路径。还指定了输入节到TEXT,DATA和BSS的默认输出节的映射。

模块视图

模块视图显示每个目标文件对链接描述文件中定义的TEXT,DATA和BSS节的贡献。通过解析映射文件中的“内存映射”部分来收集信息。感兴趣的示例行是:

 。文本._write   0x0000aca0       0x34 /cygdrive/c/Program Files (x86)/FTDI/FT90x Toolchain/Toolchain/tools/bin/../lib/gcc/ft32-elf/5.0.0/../../../../ft32-elf/lib/libstub.a(stdio.o)
                0x0000aca0                _write
 .data          0x00800084       0x10 ./main.o
                0x00800084                DataGlobal
 .data          0x00800094        0x8 ./test.o
  *(.bss)
 .bss           0x00800ce4        0x4 ./main.o
 .bss           0x00800ce8        0x4 ./test.o

字符串的链接器优化

您可能会注意到数据段的实际大小是 少于 通过汇总每个模块的数据贡献而获得的结果。这似乎是因为链接程序进行了一些优化,以合并通用字符串文字跨模块中的模块。 数据 部分。文件中的字符串文字存储在 数据.str1.4 GCC部分,并标记为链接器优化。优化之前的大小在MAP文件中通过以下方式指示 (放松之前的大小)

符号视图

为了获得符号,我们在ELF文件上运行NM并提取按大小排序的符号。的 美国证券交易委员会 列指示符号进入哪个部分(文本= T,数据= D和BSS = B)。的 全球 列标识全局符号(G),静态符号(S)和“隐藏”符号(H)。我将“隐藏”符号定义为在Map文件中可见但在NM输出中不存在的那些符号。这不是“正确的”分类,就好像您在查看ELF符号表时一样,还有许多其他符号标记为 。找到这些隐藏符号的大小有些棘手;现在,我通过减去Map文件中符号地址的大小来找到大小,同时假设所有符号的总大小等于模块大小。如果模块包含非隐藏符号,则该算法将给出错误的结果。通过查看ELF符号表中的信息,我可能会找到正确的大小;但这深陷收益递减的山谷之深,暂时还不值得细读。

符号视图中显示的符号将根据模块视图中选择的文件进行过滤。至 重启 要显示所有符号的符号视图,请单击 重置符号 按钮。可以像过滤模块视图一样过滤符号视图中的行。

实际值

由于模块和符号的总和不一定总会累加实际消耗的资源,因此第三个显示屏显示 实际 地图文件中报告的大小。这是通过将Map文件中总“段大小”的内容相加得出的。即这些行

.data           0x00800000      0xce4 load address 0x0000f080
.text           0x00000000     0xf080

观察结果

Looking at the symbol output for the test project, you can see that the names of static variables with function scope (static unsigned int FuncScopeStatic) have been mangled with a .NNNN 后缀。这就是GCC处理文件中多个具有相同名称的(静态)变量的方式。具有相同名称但定义在不同文件中的静态变量/函数不需要进行修改,因为编译器一次只能看到一个文件。

简单地将所有符号的大小相加通常不会等于模块或 实际 size of the various sections. One of the reasons is the presence of string literals that have no associated symbol name (eg: printf("I'm a nameless string");). There are also padding bytes marked as *填* 在地图文件中,该工具目前尚未计数。

移植到新目标

将工具移植到新目标时,需要做的第一件事就是告诉工具哪些部分映射到Text / Data / Bss段。该映射在工具中配置 设定值,并且该信息应在目标链接器脚本中可用。我已经为FT32和XC16配置了它。如果目标中的细分多于默认的Text / Data / Bss,则需要向ObjectListView中添加新列,并为每个新细分创建新设置。

示例PIC24(XC16)

例如,考虑一下 PIC24FJ256GB106.

/*
** Memory Regions
*/
MEMORY
{
  data (a!xr) : ORIGIN = 0x800, LENGTH = 0x4000
  重启 : ORIGIN = 0x0, LENGTH = 0x4
  ivt : ORIGIN = 0x4, LENGTH = 0xFC
  _reserved : ORIGIN = 0x100, LENGTH = 0x4
  aivt : ORIGIN = 0x104, LENGTH = 0xFC
  program (xr) : ORIGIN = 0x200, LENGTH = 0x2A9F8
  CONFIG3 : ORIGIN = 0x2ABFA, LENGTH = 0x2
  CONFIG2 : ORIGIN = 0x2ABFC, LENGTH = 0x2
  CONFIG1 : ORIGIN = 0x2ABFE, LENGTH = 0x2
}

The Program Flash ranges from 0x00 to 0x2AC00 (on a PIC24 program memory is organized as words 和 each instruction is 24 bits wide), 和 contains the memory regions 重启, ivt, _reserved, aivt, program 和 the config words. Next we need to find the segment to section mapping, i.e. if you look deeper you'll see that the sections [.reset, .init, .user_init, .handle, .isr, .libc, .libm, .libdsp, .lib, usercode, userconst] end up going into the program memory which we refer to as 文本 分割。类似地,需要映射数据(ndata,数据)和BSS(nbss,bss)。最后将路径设置为NM和OBJDUMP- C:\ Program Files(x86)\ Microchip \ xc16 \ v1.25 \ bin \ xc16-xxx.exe

  .reset :
  {
        SHORT(ABSOLUTE(__reset));
        SHORT(0x04);
        SHORT((ABSOLUTE(__reset) >> 16) & 0x7F);
        SHORT(0);
  } >reset
  。文本 :
  {
        *(.init);
        *(.user_init);
        KEEP (*(.handle));
        KEEP (*(.isr*));
        *(.libc) *(.libm) *(.libdsp); /* keep together in this order */
        *(.lib*);
  } >program
  usercode :
  {
        *(usercode);
  } >program

映射也可以在Map文件中看到:

Program Memory  [Origin = 0x200, Length = 0x2a9f8]
section                    address   length (PC units)   length (bytes) (dec)
-------                    -------   -----------------   --------------------
.text                        0x200               0xf3a          0x16d7  (5847)
.const                      0x113a                0xb6           0x111  (273)
.dinit                      0x11f0                0xde           0x14d  (333)
.text                       0x12ce                0x72            0xab  (171)
                     Total program memory used (bytes):         0x19e0  (6624) 2%
Data Memory  [Origin = 0x800, Length = 0x4000]
section                    address      alignment gaps    total length  (dec)
-------                    -------      --------------    -------------------
.ndata                       0x800                   0             0x8  (8)
.nbss                        0x808                   0             0x4  (4)
.ndata                       0x80c                   0             0x4  (4)
.nbss                        0x810                   0             0x2  (2)
.data                        0x812                   0            0xc4  (196)
.bss                         0x8d6                   0             0x6  (6)
.data                        0x8dc                   0             0x4  (4)
.bss                         0x8e0                   0             0x2  (2)
                        Total data memory used (bytes):           0xe2  (226) 1%

I've specified the superset of the FT32 和 PIC24 mapping as default attributes in 设定值.cs so everything should work out of the box for FT32/PIC24. PIC32 has a more complicated map, so I haven't gotten around to that yet.

[DefaultValue("。文本, .reset, .init, .user_init, .handle, .isr, .libc, .libm, .libdsp, .lib, usercode, userconst"), Category("Segment To Section Map")]

参考文献

  1. 联用
  2. ELF规范v1.2
  3. DWARF规范v4
  4. DWARF调试格式简介-Michael J. Eager
  5. 使用创建的ASCII图 JavE
  6. 更好的列表视图-ObjectListView

[-]
评论者 斯蒂芬布三月25,2019

戈文德!已经有一段时间了,我希望一切对您都好。您知道您的这篇文章是 最受欢迎/观看次数最多的文章 在过去30天的博客部分中?如果您有灵感在EmbeddedRelated上撰写更多文章,那将不胜感激。保重戈文德。

[-]
评论者 及时虫2019年4月28日

很高兴知道我现在是一位有影响力的人:P希望将来能写更多东西,不幸的是,工作和其他承诺使我很忙。

[-]
评论者 SANEESH MP2016年8月24日
嗨,Govind Mukundan,

这里讨论了很棒的话题,谢谢分享。
我正在开发一个Java程序来绘制来自dsPIC目标的内部变量。我有一个问题,要弄清楚我需要绘制的全局符号的长度和长度,使用该工具有助于理解这一点。
有没有简单的方法可以将此集成到Java中。

谢谢
萨内什
[-]
评论者 及时虫2017年5月10日
抱歉,您的回复很晚,对于您的评论,我似乎还没有收到任何通知。最简单的方法是使用微芯片提供的“ nm”实用程序以及dsPIC工具链。您可以使用-S选项和.elf文件作为输入来运行NM。例如,这就是我在MapViewer源代码中包含的测试项目上运行NM的方法:
$ ft32-elf-nm -S MapParser.elf
00800a2c 00000010 d blanks.4267
00800d20 00000004 B BSSGlobal
0000008c t codestart
00800084 00000004 D DataGlobal
00800d24 00000004 B errno

第一列是地址,第二列是大小,第三个字符表示符号所在的位置。在Java程序中,您只需解析此输出。

[-]
评论者 saneeshmp2017年5月10日

谢谢戈文德,

您是否愿意共享电子邮件以进行任何查询。


Rgds
萨内什

[-]
评论者 及时虫2017年5月10日

govindmukundan at g ** il

[-]
评论者 pradeepgr1232016年11月21日

如果我们有两个地址空间之间有间隔的闪存,如何自动找到要闪存的代码。


例如:我有从存储器0x0到0x5000的Flash A,从存储器0x6000到0x11000的Flash B.我如何编写应该将代码段放入Flash A的链接描述文件,一旦Flash A装满,则应位于Flash B中


像下面的东西

部分

。文本

} > FLASHA | FLASHB

[-]
评论者 及时虫2017年5月10日

抱歉,您的回复很晚,对于您的评论,我似乎没有任何通知。这是一个很好的问题,恐怕我不知道GNU-LD链接器是否支持自动将数据重新定位到不同的区域。当然,您可以通过定义两个部分并在每个部分中添加一堆* .o来手动完成此操作。最好的选择是看看其他具有类似处理器体系结构的项目,然后看看它们是如何做到的。

[-]
评论者 丹尼尔2018年2月8日
嗨,戈文德,

对于那些无法自行编译C#的人来说,是否有一个.exe应用程序?

谢谢!

[-]
评论者 丹尼尔2018年2月9日

没关系,我启动了Visual Studio,打开了解决方案-它的构建没有问题。谢谢!

[-]
评论者 及时虫2018年2月12日

您好Danijel, 

我很高兴您知道了!我添加了预建 发布 现在。

问候

G

[-]
评论者 丹尼尔2018年2月12日

感谢那。 

我遇到了一些问题,它经常停转并崩溃。

我在frmMain.cs中遇到异常,因此添加了一行: 如果(list.Count()> 0) 这似乎救了我。

如果(list.Count()> 0)
    如果(list.FirstOrDefault()。ModuleName!= null)
        _tree = lv_Init(list.FirstOrDefault()。ModuleName);

我对此不确定,请检查。

[-]
评论者 丹尼尔2018年2月12日

对不起,忘记了另一件事: 

我评论了

// Progress_indication(); //更新用户界面

也在frmMain.cs中。

我认为这导致了我的停滞和“无响应”。



[-]
评论者 丹尼尔2018年2月12日

是否可以添加一个自定义的第4列,以便用户可以在其中放置其他部分数据(.debbug等)?只是徘徊...对我而言很有用。

[-]
评论者 及时虫2018年2月12日

是的,您可以轻松地从Visual Studio中添加/删除列。要编辑的文件是frmMain.cs。当然,您必须提供要填充到列中的数据。

[-]
评论者 丹尼尔2018年2月12日

感谢您的快速回复!会尝试的。 

[-]
评论者 霍萨姆·阿尔佐莫2020年4月30日

尝试将“ --cref”添加到链接器选项时,出现以下错误

arm-none-eabi-gcc:错误:无法识别的命令行选项'--cref';你是说'--xref'吗?

'--cref'等价于'--xref'吗?


[-]
评论者 及时虫2020年5月1日

是的,它似乎等同于GNU LD的--cref的ARM链接器- http://infocenter.arm.com/help/index.jsp?topic=/co...

[-]
评论者 安德森2020年5月19日

非常好的工具

您是否认为可以升级/修改它以进行静态堆栈分析....解析gcc可以为每个模块(或我应该说的功能)创建的* .su文件。

//gcc.gnu.org/onlinedocs/gnat_ugn/Static-Sta...

可以在这里添加类似的内容:

//mcuoneclipse.com/2015/08/21/gnu-static-sta...

我对C#的了解不足以真正做到这些事情。

谢谢,这对于为我的项目取回一些宝贵的RAM非常有用....以不同的方式对其进行编码。


[-]
评论者 及时虫2020年5月20日

感谢您的反馈和想法,是的,尤其是在调用图信息中添加堆栈使用量确实非常有用。 

如果您已经知道C,那么C#应该不会太困难:)

要发布对评论的回复,请单击每个评论所附的“回复”按钮。要发布新评论(而不是回复评论),请查看评论顶部的“写评论”标签。

注册后,您可以参加所有相关网站上的论坛,并获得所有pdf下载的访问权限。

注册

我同意 使用条款隐私政策.

试试我们偶尔但很受欢迎的时事通讯。非常容易退订。
或登录