nm和map文件-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

在Linux中我们可以使用nm命令来查看程序的符号表包括全局变量。这个命令可以帮助我们查看全局变量的地址。然而全局变量的地址是否相邻取决于编译器的实现和优化。

下面是一个简单的例子首先我们创建一个C程序定义两个全局变量

int global_var1;
int global_var2;

int main() {
    return 0;
}

然后我们编译这个程序并生成map文件

gcc -o test test.c -Wl,-Map,test.map

最后我们使用nm命令查看全局变量的地址

nm test | grep 'global_var'

这将显示全局变量的地址。你可以比较这两个地址来看它们是否相邻。但是请注意即使这两个变量在源代码中是相邻的它们在内存中的地址也可能不是相邻的这取决于编译器如何分配内存。此外全局变量的大小也会影响它们的地址。例如如果一个变量是int类型另一个变量是double类型那么它们的地址就不可能是相邻的因为double类型的变量需要更多的内存空间。所以即使两个全局变量在源代码中是相邻的它们在内存中的地址也可能不是相邻的。这就是为什么我们需要使用nm命令来查看实际的内存地址。


我定义了一个static的变量如static unsigned int global_var8;通过nm test | grep 'global_var'查找的时候能看到但是在map文件里找不到是什么问题呢?

在Linux中static关键字会使变量的作用域限制在声明它的文件内。因此static变量不会出现在链接器的符号表中也就不会出现在map文件中。这是因为链接器只需要处理全局符号即那些可能需要在一个文件中定义在其他文件中引用的符号。

然而你可以使用nm命令查看包含static变量的目标文件.o文件这样就可以看到static变量了。但是请注意这些地址是在链接前分配的所以它们在最终的可执行文件中可能会有所不同。

此外如果你的static变量没有被使用编译器可能会优化掉它因此即使在目标文件中也看不到它¹。你可以尝试关闭优化选项看看是否能在目标文件中看到你的static变量。


global_var10是一个在函数xiaomi_func内部定义的静态变量。这意味着这个变量只在这个函数内部可见而在函数外部是看不到的。然而由于它是静态的所以它在程序的整个生命周期内都存在。

当你使用nm命令查看符号时你会看到global_var10.0。这是因为编译器对局部静态变量进行了名字修饰name mangling。这是一种编译器用来确保每个符号名称在内部都是唯一的技术。编译器会在变量名后面添加一个点和一个序列号。这就是你看到的.0

所以global_var10.0实际上就是你在xiaomi_func函数内部定义的静态变量global_var10


在嵌入式系统中我们可以使用链接脚本来配置特定的段section并使用特定的属性来将变量设置为归属于特定的段。以下是一个简单的示例展示了如何在GCC编译器中进行操作

// 链接脚本
SECTIONS
{
    . = 0x20000000;
    .data : { *(.data) }
    .my_section : { *(.my_section) }
    .bss : { *(.bss) }
}

// C代码
int var1 __attribute__((section(".data"))) = 0;        // 将var1放在.data段
int var2 __attribute__((section(".my_section"))) = 0;  // 将var2放在.my_section段

在这个例子中我们首先定义了一个链接脚本它指定了内存的起始地址0x20000000然后定义了三个段.data.my_section.bss。然后在C代码中我们使用__attribute__((section(".section_name")))来指定变量应该放在哪个段中。

请注意这只是一个基本的示例实际的链接脚本可能会更复杂并且可能需要包含更多的信息例如段的大小对齐要求等。此外不同的编译器可能会有不同的语法所以请确保查阅您所使用的编译器的文档。


在Keil中配置段通常需要通过链接脚本Linker Script来完成。链接脚本可以定义内存区域的布局包括代码段.text、数据段.data、未初始化数据段.bss等。以下是一个简单的示例

LR_IROM1 0x08000000 0x00040000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00040000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM1 0x20000000 0x00010000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

在这个例子中我们定义了两个内存区域一个是Flash从0x08000000开始大小为0x00040000另一个是RAM从0x20000000开始大小为0x00010000。在Flash区域中我们存储了代码和只读数据在RAM区域中我们存储了读写数据和未初始化数据。

然后你可以使用__attribute__((section(".section_name")))来将特定的变量或函数放入特定的段中。例如

int var __attribute__((section("ER_IROM1"))) = 0;

这将会把变量var放入ER_IROM1段中。

请注意这只是一个基本的示例实际的链接脚本可能会更复杂并且可能需要包含更多的信息例如段的大小对齐要求等。此外不同的编译器可能会有不同的语法所以请确保查阅您所使用的编译器的文档。


在嵌入式系统中可以将data段和bss段配置到SRAM或PSRAM中。这通常涉及到链接脚本的修改以便在链接过程中将特定的段定位到特定的内存区域。

以下是一个简单的示例展示了如何在链接脚本中配置段的位置。这个示例假设你正在使用GNU链接器并且你的链接脚本可能看起来像这样

MEMORY
{
  sram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
  psram (rwx) : ORIGIN = 0x60000000, LENGTH = 4M
}

SECTIONS
{
  .data : {
    *(.data)
  } >sram

  .bss : {
    *(.bss)
  } >psram
}

在这个示例中我们首先定义了两个内存区域一个是SRAM起始地址为0x20000000长度为64KB另一个是PSRAM起始地址为0x60000000长度为4MB。

然后在SECTIONS部分我们指定.data段应该链接到SRAM中.bss段应该链接到PSRAM中。*(.data)*(.bss)是通配符匹配所有的.data.bss输入段。

这只是一个基本的示例实际的链接脚本可能会更复杂包括处理初始化数据、复制到SRAM的数据等。

请注意这需要对链接脚本有深入的理解并且可能需要根据你的具体硬件和编译器进行调整。如果你不熟悉这个过程可能需要寻求专业的嵌入式系统开发人员的帮助。在进行这些更改时请务必谨慎因为错误的配置可能会导致程序无法正确运行。


SRAM和PSRAM都需要源源不断的供电来保持其存储的数据。然而它们在功耗上有所不同。

  • SRAM静态随机存取存储器不需要进行刷新操作来保持数据因此其功耗相对较小。然而SRAM的存储单元由6个晶体管构成这使得其在相同的存储容量下需要更多的晶体管从而产生较多的热量。

  • PSRAM伪静态随机存取存储器具有与SRAM相同的接口协议但其内核架构却与DRAM动态随机存取存储器相同。这意味着PSRAM不需要复杂的内存控制器定期刷新数据但其内核是DRAM架构。因此PSRAM的功耗可能会介于SRAM和DRAM之间。

总的来说SRAM和PSRAM的具体功耗可能会因其具体的使用情况和设计而有所不同。如果你正在考虑在嵌入式系统中使用这些存储器可能需要根据你的具体需求例如对速度、功耗、成本和存储容量的需求来选择最适合的存储器类型。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6