何时使用.ARM.exidx?

15

我正在使用Contiki 2.7版本,使用mbxxx目标进行开发。在构建代码时,链接器报告了.ARM.exidx和.data节的重叠问题。经过对链接器脚本contiki-2.7/cpu/stm32w108/gnu-stm32w108.ld的一些修改后,我用以下代码解决了这个问题:

__exidx_start = .;
__exidx_end = .;

使用:

.ARM.exidx : {
    __exidx_start = .;
    *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    __exidx_end = .;
} >ROM_region

之后我试图使用objdump -h查看其他示例应用程序的标头列表时,我没有找到这个特定的.ARM.exidx部分,但它存在于我的应用程序中。通过搜索关于.ARM.exidx的信息,我了解到它用于一些C++异常处理。既然我的代码是纯C代码,为什么我的代码中会出现这个部分?一般情况下,.ARM.exidx何时出现在代码中,其实用是什么?


不,我没有这样的编译器选项。我实际上正在使用AxTLS API,并摘掉了证书处理代码并将其移植到Contiki。在进一步挖掘中,我发现了bigint实现中的可疑行为。简而言之……这是bigint.c文件中一个函数的主体:

static bigint *bi_int_multiply(BI_CTX *ctx, bigint *bia, comp b)
{
   int j = 0, n = bia->size;
   bigint *biR = alloc(ctx, n + 1);
   comp carry = 5;
   comp *r = biR->comps;
   comp *a = bia->comps;

   check(bia);

   /* clear things to start with */
   memset(r, 0, ((n+1)*COMP_BYTE_SIZE));


   do
   {
       long_comp tmp = *r + (long_comp)a[j]*b + carry;
   //    *r++ = (comp)tmp;              /* downsize */
       carry = (comp)(tmp >> COMP_BIT_SIZE);
   } while (++j < n);

  // *r = carry;
  bi_free(ctx, bia);

  return trim(biR);
}

如果取消注释(r变量的赋值),则会出现".ARM.exidx",否则不会出现。这个能解释吗?
我在alloc()函数实现中没有发现异常使用的内容。代码的某些其他区域中使用了2个alloca()的引用,我用malloc()free()替换了它们,但也没有修复问题。alloc()实现只调用了malloc()realloc()free()

你好 @user2668988!提醒一下,如果你想在问题中添加更多细节,可以在这里编辑你的问题。 - Chris Forrence
biR 是否使用 alloca() 或者 alloc() 是如何实现的?编译器可能会使用相同的 C++ 机制来跟踪 alloca() 类型的分配。当您注释掉 r 并启用优化时,alloc() 将不会发生。 - artless noise
我在alloc()的实现中没有发现任何不寻常的用法。代码的某个独立区域中使用了2个alloca()引用,但是我将它们替换为malloc()free()也并没有解决问题。alloc()的实现只调用了malloc()realloc()free() - user2668988
3个回答

22

.ARM.exidx 是包含用于展开堆栈的信息的部分。如果您的 C 程序有打印堆栈回溯的函数,则这些函数可能依赖于该部分的存在。

可以查找编译器选项中的 -funwind-tables-fexceptions 标志。


我相信-funwind-tables选项默认已开启,至少在近年的CodeSourcery工具链中是这样。没有这个选项,在调试器中进行回溯会很麻烦。 - ams
如何禁用此选项? - user2668988
找到标志并将其移除? - tangrs
1
你不能“删除”隐式标志,但有时可以添加计数器标志(例如-fno-unwind-tables)。 - kwesolowski

7

这个特性在'C'中使用。ARM APCS仅使用帧指针恢复堆栈,而新的AAPCS偶尔使用表格。堆栈展开、信号处理程序和其他异步'C'功能使用这些机制。对于裸机嵌入式设备,它可用于跟踪堆栈。例如,Linux的unwind.c使用exidxextab部分来进行堆栈跟踪。

简言之,exidx是一个排序过的例程起始地址和extab表索引的表格。通过exidx进行二进制搜索可以找到相应的extab条目。extab条目提供有关此例程中的堆栈情况Note1。它提供有关例程正在堆栈上存储什么的详细信息。

如果取消注释已注释掉的部分(变量赋值r),则.ARM.exidx就会出现,否则不会!现在能解释一下吗?

当你有语句*r++ = (comp)tmp;时,编译器无法将所有变量保存在寄存器中,需要使用堆栈(或至少fp)。这导致它发出exidxextab数据。


有一些解决方案。如果您不需要堆栈跟踪或异步功能,则可以丢弃exidxextab。使用gnu工具/gcc使用-mapcs-frame可以进行更简单的堆栈展开;然后fp将始终用于存储先前的堆栈帧(其中存储其调用者的fp等)。实际的表格并不那么大,解开的例程相当简单。拥有缓存和通常更多内存的Cortex-A CPU上可能会产生小于3%的开销,这样的表格不会污染正常的程序路径或使用像-mapcs-frame这样的寄存器。

参考:ATPCS Link and frame pointer explained
                  Structure of ARM extab

Note1: 这是在序言对堆栈进行调整之后。
Note2: ex用于异常,但它不仅限于C++异常。


2

补充 tangrs 的回答,如果你输入 gcc -v 命令,可以查看编译时使用的默认选项。

GCC 的所有选项(隐式和显式)都会传递给 GCC 的 cc1 程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接