C语言 链接器如何确定哪些自定义节是只读的?

cvxl0en2  于 2023-01-04  发布在  其他
关注(0)|答案(1)|浏览(159)

如果你在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是常数)和gcc9.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))结合使用,以确保节是可写的。

mctunoxg

mctunoxg1#

  • 成就解锁:事实上,它是编译器-触发编译器中的错误。*

这不是链接器的工作,而是编译器的工作。
链接器只是合并它被告知要合并的部分,以及它们的属性(在不匹配的情况下使用最大权限),然后将它们布局到可执行段中;或者在可重定位链接的情况下,简单地合并相同命名的部分(对于属性具有相同的行为)。
编译器的工作就是把你的程序变量放到节中,并给这些节分配适当的属性。不幸的是,当用户请求把需要不同节属性的变量放到同一个节中时,Clang 13和更早的版本似乎会感到困惑。如果你在链接之前查看一下目标文件,会更清楚:

$ nix shell 'nixpkgs#binutils' 'nixpkgs#clang_13'
$ clang -mcmodel=large -O1 -c test.c
$ readelf -S test.o
There are 11 section headers, starting at offset 0x2a8:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
[...]
  [ 3] .rela.text        RELA             0000000000000000  000001d0
       0000000000000030  0000000000000018          10     2     8
  [ 4] my_nonconst_[...] PROGBITS         0000000000000000  00000080
       0000000000000058  0000000000000000   A       0     0     16
  [ 5] .comment          PROGBITS         0000000000000000  000000d8
       0000000000000016  0000000000000001  MS       0     0     1
[...]
$ ^D

(我试着看看您是否可以通过声明another_thing来解决这个问题,这需要先声明一个WA节。您做不到,而且some_thing甚至以一个更低的地址结束。)
Clang 14及更高版本正确地处理了这个问题,虽然有些混乱,但它生成了两个名称相同但属性不同的部分:

$ nix shell 'nixpkgs#binutils' 'nixpkgs#clang_14'
$ clang -mcmodel=large -O1 -c test.c
$ readelf -S test.o
There are 12 section headers, starting at offset 0x2c0:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
[...]
  [ 3] .rela.text        RELA             0000000000000000  000001e8
       0000000000000030  0000000000000018   I      11     2     8
  [ 4] my_nonconst_[...] PROGBITS         0000000000000000  00000080
       0000000000000028  0000000000000000   A       0     0     16
  [ 5] my_nonconst_[...] PROGBITS         0000000000000000  000000b0
       0000000000000028  0000000000000000  WA       0     0     16
  [ 6] .comment          PROGBITS         0000000000000000  000000d8
       0000000000000016  0000000000000001  MS       0     0     1
[...]
$ ^D

如果你把这个目标文件链接到一个可执行文件中,那么在前一种错误的情况下,my_nonconst_sectionA属性结束(duh),所以你得到一个segfault,而在后一种正确的情况下,它以WA结束(由于合并),所以可执行文件正常工作。

**P.  S.**有趣的是,因为GNU汇编器不能在单个对象中支持相同名称的部分,而LLVM-MC遵循了这一点,所以clang -c中没有bug的对象文件现在与clang -S打印的汇编器源代码不匹配,因为后者在一次声明两个变量之前使用了.section my_nonconst_section,"aw",@progbits

相关问题