【问题标题】:What might be the point in putting a variable exactly in the "STACK" section with __attribute__ ((section("STACK"))?使用 __attribute__ ((section("STACK")) 将变量完全放在“STACK”部分中可能有什么意义?
【发布时间】:2012-03-29 08:32:05
【问题描述】:

In gcc doc 给出了使用section 的一个原因。这个原因是to map to special hardware。但这似乎不是我的情况。

所以我给了一个任务来修改我们在项目中使用的共享库。它是一个 Linux 库。库中的变量声明让我感到困惑。它们看起来像这样(大致):

static int my_var_1 __attribute__((section("STACK"))) = 0;


更新 1:
以这种方式定义的变量有十几个(__attribute__((section("STACK")))


更新 2:
my_var_1 不是常数。 my_var_1 可能会在初始化期间更改代码:
my_var_1 = atoi(getenv("MY_VAR_1") ? getenv("MY_VAR_1") : "0");

稍后在库中它是这样使用的:

inline void do_something() __attribute__((always_inline));
inline void do_something()
{
  if (my_var_1)
    do_something_else();
}


使用__attribute__((section("STACK"))) 有什么意义?我知道section 告诉编译器在特定部分中放置一个变量。但是,将static int 完全放在“堆栈”部分可能有什么意义?


更新 3
这些行摘自readelf -t my_lib.so 的输出
  [23] .got.plt
       PROGBITS         00000000002103f0  00000000000103f0  0
       00000000000003a8 0000000000000008  0                 8
       [0000000000000003]: WRITE, ALLOC
  [24] .data
       PROGBITS         00000000002107a0  00000000000107a0  0
       00000000000000b0 0000000000000000  0                 16
       [0000000000000003]: WRITE, ALLOC
  [25] STACK
       PROGBITS         0000000000210860  0000000000010860  0
       00000000000860e0 0000000000000000  0                 32
       [0000000000000003]: WRITE, ALLOC
  [26] .bss
       NOBITS           0000000000296940  0000000000096940  0
       0000000000000580 0000000000000000  0                 32
       [0000000000000003]: WRITE, ALLOC


更新 4
设法从共享库的作者那里获取信息。 __attribute__((section("STACK"))) 被添加,因为他没有设法在 Solaris 上构建库。然后他找到了这个解决方法。在解决方法之前,my_var_1 的定义如下:
int my_var_1 = 0;

一切正常。然后他更改了它,因为 my_var_1 实际上只在这个翻译单元中需要:

static int my_var_1 = 0;

在那次更改之后,他没有设法在 Solaris 上构建库。所以他添加了__attribute__((section("STACK"))),它在某种程度上有所帮助。


【问题讨论】:

  • 您的项目是在普通的 x86 机器上运行,还是类似于 ARM 片上系统 (SoC)?你能找到一个链接器脚本(由 ld 使用,因此通常以“.ld”结尾)吗?这将明确地将堆栈部分放入内存区域,因此找到链接描述文件将提供更多线索。
  • 该程序用于普通的 x64_86 服务器和 HP-UX Itanium2。没有 ARM 系统。
  • 可能值得在您的问题中添加有关机器的信息。我可以想象 Itanium2 可能触发某些东西(我对此知之甚少,但它是不同的 :-)

标签: c linux gcc shared-libraries


【解决方案1】:

首先STACK 部分不会是任何正在运行的任务的堆栈。

将变量、函数放在特定的部分允许为它们选择一个内存区域(感谢链接描述文件)。在某些(主要是嵌入式)架构上,您希望将经常访问的数据放在更快的内存中。

其他解决方案,某些开发后链接脚本会将所有STACK 部分设置为1:开发软件将始终执行do_something_else()。并且发布的软件可能会保持默认值0。

另一种可能性,如果STACK 部分中还有其他变量,开发人员希望将它们靠近内存。 STACK 部分中的所有变量将彼此靠近。也许是缓存优化?

【讨论】:

  • 我编辑了我的问题(更新 2),以便明确您的建议 Other solution, some development post-link script ...。对不起,我应该提到my_var_1 is not a constant
  • @skwllsp 有链接描述文件吗?如果没有链接描述文件,一个部分只会帮助对同一区域中的所有变量进行分组。 readelf -t your.so 可能会提供STACK 部分所在的信息。
  • 不,没有链接描述文件。
  • 如果没有链接器脚本,您的(特定)动态链接器可能会将共享库的 STACK 部分放在特定位置(快速 RAM?)
  • 据我所知,我们的程序和这个共享库都没有使用特殊的位置。其实我相信你关于缓存优化的想法可能是正确的。
【解决方案2】:

可能有很多原因,没有细节很难说。一些原因可能是:

  1. 标记为 STACK 的部分在运行时链接到紧密耦合的内存,其访问时间比其他 RAM 更快。将堆栈映射到这样的 RAM 以避免函数调用期间的停顿是有意义的。现在,如果您突然有一个经常被访问的变量,并且您想将其映射到相同的快速访问 RAM,将其放在与堆栈相同的部分是有意义的。

  2. 标记为 STACK 的部分可能会映射到内存的其他部分可能无法访问时可访问的内存区域。例如,引导加载程序需要先初始化内存控制器,然后才能访问 RAM。但是你真的希望能够用 C 语言编写代码,这需要堆栈。因此,您可以找到一些特殊的内存(例如将数据缓存编程为回写模式)并将堆栈映射到那里,这样您就可以运行代码来使内存控制器工作,这样您就可以使用 RAM。再说一次,如果您现在碰巧有一个在 RAM 可用之前仍需要访问的全局变量,您可能会决定将它放在 STACK 部分中。

如果堆栈部分不仅用于堆栈,更好的程序员会将堆栈部分重命名为其他内容。

【讨论】:

    【解决方案3】:

    在某些操作系统中,相同的寻址空间区域用于每个线程的堆栈;当执行在线程之间切换时,该空间的映射会相应更改。在这样的系统上,每个线程都有自己独立的一组位于该地址空间区域内的任何静态变量。将需要为每个线程单独维护的变量放在这样的地址范围内,将避免需要在每次任务切换时手动交换它们。

    另一个强制变量进入堆栈区域的偶尔用途是添加堆栈标记(可以定期检查变量以查看堆栈溢出是否破坏了它们)。

    第三种用途发生在 8086 和 80286 平台(可能不是该系列中较晚的芯片):8086 和 80286 仅限于有效访问四个段中的内容,而无需重新加载段寄存器。如果代码需要做与

    等价的事情
    for (n=0; n<256; n++)
      *dest++ = xlat[*src++];
    

    并且没有任何项目可以放入代码段,能够强制其中一项进入堆栈段可以使代码快得多。实现加速需要手写汇编代码,但它可能非常庞大(在我在 8086 上完成的某些实际情况下几乎是两倍,在 80286 上的某些情况下可能更大)。

    【讨论】:

      猜你喜欢
      • 2016-06-10
      • 2014-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-29
      • 1970-01-01
      • 2012-05-23
      • 1970-01-01
      相关资源
      最近更新 更多