如果你在C语言中定义了一个全局变量的自定义节,并且在链接器脚本中定义了一个自定义输出节,那么链接器(?)如何确定这个节的属性(W =可写,A =可分配,......)?
问题和MWE
我目前面临的问题是,我将同一个节分配给两个全局变量,其中一个是常量(在它的用法中,而不是程序代码中),而另一个不是。最后,它们都结束在一个只能分配但不能写入的节中,程序以分段错误终止。
示例程序(test.c
):
static double some_thing[5] __attribute__ ((section ("my_nonconst_section"))) = {2.0, 4.0, 6.0, 8.0, 10.0};
static double another_thing[5] __attribute__ ((section ("my_nonconst_section")));
int main(int argc, char const *argv[]) {
another_thing[1] = some_thing[argc];
return another_thing[argc] == 0.0;
}
我的自定义链接器脚本扩展(linkerscript.ld
,注意自定义地址对我来说至关重要,这就是为什么我首先使用这些节):
SECTIONS {
. = 0x0000001b000002e0;
my_nonconst_section : {KEEP(*(my_nonconst_section))}
/* . = 0xAddressForThisSection;
my_const_section : {KEEP(*(my_const_section))}
... */
}
INSERT AFTER .gnu.attributes;
我使用clang(经过测试的10.0.0-4ubuntu1
和自构建的12)来编译/链接它与我的链接器脚本(clang也不是可选的,见下文):clang -mcmodel=large -O1 test.c -Wl,-Tlinkerscript.ld -o test.tmp
然后执行它:./test.tmp
但是,我注意到clang -O0
(它没有推导出some_thing
是常数)和gcc
(9.4.0
,具有任何优化级别)没有表现出这种行为。
我使用的链接器是GNU ld (GNU Binutils for Ubuntu) 2.34
,但是我可以看到与gold
链接器相同的效果。
我可以影响自定义链接器部分的属性吗(最好在链接器脚本中定义)?我可以在可写部分中不写一些变量。
背景/背景:
我正在编写一个(LLVM/clang)编译器通道,它使用自定义链接器部分注解全局变量,我在自定义链接器脚本(它通过这些部分扩展默认链接器脚本)中定义这些全局变量的链接器输出部分,类似于上面描述的内容。
通过查看全局变量的属性来区分常量和非常量全局变量。如果是常量,则选择常量的节标签,否则选择包含可写数据的节标签。但是,在添加节注解 * 之后 *,另一个编译器通道能够显示其中一个变量--用(思想上)非常量部分--实际上只是读取,因此传递将其标记为常量。
结果是包含标记为const
的全局变量的部分变为只读,而它仍然包含非常数全局变量。在程序执行期间,试图在此部分写入另一个全局变量会导致分段错误(如预期)。
我确定nm
的两个变量都是只读的:
0000001b00000310 r another_thing
0000001b000002e0 r some_thing
截面如下所示(由readelf -S
确定):
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[..] my_nonconst_section PROGBITS 0000001b000002e0 0032e0 000058 00 A 0 0 16
通常,我希望非常数部分使用Flg = WA
,常量部分使用Flg = A
。
备注
目前,我不需要手写完整的链接器脚本,并且编译器通道可以兼容处理已经有节注解的C源代码,保留这个属性会很好。我看到可以使用MEMORY
指令完全定义内存布局,但是据我所知,这需要定义所有节的内存,这是我不想做的。目前,由于我使用"绑定"来确定节的地址,我不能(据我所知)将绑定与命名内存(即属性特性> (RW)
)结合使用,以确保节是可写的。
1条答案
按热度按时间mctunoxg1#
这不是链接器的工作,而是编译器的工作。
链接器只是合并它被告知要合并的部分,以及它们的属性(在不匹配的情况下使用最大权限),然后将它们布局到可执行段中;或者在可重定位链接的情况下,简单地合并相同命名的部分(对于属性具有相同的行为)。
编译器的工作就是把你的程序变量放到节中,并给这些节分配适当的属性。不幸的是,当用户请求把需要不同节属性的变量放到同一个节中时,Clang 13和更早的版本似乎会感到困惑。如果你在链接之前查看一下目标文件,会更清楚:
(我试着看看您是否可以通过声明
another_thing
来解决这个问题,这需要先声明一个WA
节。您做不到,而且some_thing
甚至以一个更低的地址结束。)Clang 14及更高版本正确地处理了这个问题,虽然有些混乱,但它生成了两个名称相同但属性不同的部分:
如果你把这个目标文件链接到一个可执行文件中,那么在前一种错误的情况下,
my_nonconst_section
以A
属性结束(duh),所以你得到一个segfault,而在后一种正确的情况下,它以WA
结束(由于合并),所以可执行文件正常工作。**P. S.**有趣的是,因为GNU汇编器不能在单个对象中支持相同名称的部分,而LLVM-MC遵循了这一点,所以
clang -c
中没有bug的对象文件现在与clang -S
打印的汇编器源代码不匹配,因为后者在一次声明两个变量之前使用了.section my_nonconst_section,"aw",@progbits
。