c - 奇怪的只读符号显示为 nm 中的初始化数据部分 (D)
问题描述
我注意到gcc
(但不是clang
),const
(只读)初始化数据对象不再显示为
R
数据对象nm
,而是成为D
(初始化部分)对象。
这表明数据对象将被放置在可写内存中,但是,当同一个对象文件与gcc
or clang
(但不是tcc
)链接时,它似乎无论如何都被放置在只读内存中。
clang
似乎没有使用这些奇怪D
的只读符号(而是对象仍然存在R
)。Tinycc 确实也将这些对象变成了D
符号,但这些D
符号似乎没有那种奇怪的属性,导致链接器将它们放入只读内存中。
你能解释一下这里发生了什么吗?
下面的脚本演示了在所有组合中用作编译器和链接器的 gcc、clang 和 tinycc 的行为:
#!/bin/sh -eu
cat > file.c <<EOF
struct obj { void (*fnptr)(void); int z; };
static void fn0(void) { }
const struct obj constInitedReadonlyObject = { 0, 42 };
const struct obj readonlyObject = { &fn0, 42 };
int main()
{
int volatile*z = (int volatile*)&readonlyObject.z;
*z = 1000;
}
EOF
for cc in gcc tcc clang; do
$cc -c file.c
echo cc=$cc type=$( nm file.o |grep readonlyObject |cut -d ' ' -f 2 )
for ld in gcc tcc clang; do
$ld file.o
printf '\t%s\n' "ld=$ld $(if ./a.out 2>/dev/null; then echo NOTHING; else echo FAULT; fi)"
done
done
我的系统上的输出:
cc=gcc type=D
ld=gcc FAULT
ld=tcc NOTHING
ld=clang FAULT
cc=tcc type=D
ld=gcc NOTHING
ld=tcc NOTHING
ld=clang NOTHING
cc=clang type=R
ld=gcc FAULT
ld=tcc FAULT
ld=clang FAULT
编辑:对目标文件执行 readelf -s 并对两个数据对象进行 grepping 会产生:
clang
4: 0000000000000000 16 OBJECT GLOBAL DEFAULT 4 constInitedReadonlyObject
6: 0000000000000010 16 OBJECT GLOBAL DEFAULT 4 readonlyObject
gcc
11: 0000000000000000 16 OBJECT GLOBAL DEFAULT 5 constInitedReadonlyObject
12: 0000000000000000 16 OBJECT GLOBAL DEFAULT 6 readonlyObject
tcc
3: 0000000000000000 16 OBJECT GLOBAL DEFAULT 3 constInitedReadonlyObject
4: 0000000000000010 16 OBJECT GLOBAL DEFAULT 3 readonlyObject
我猜不同的数字(在名称为 Ndx 的列中(未显示))与行为有关。
解决方案
很可能您的 gcc 配置有--enable-default-pie
(gcc -v
来检查)。
在PIE中,readonlyObject
需要在程序启动时可写,以允许动态重定位处理代码将地址fn0
写入其第一个字段。为此,gcc 将这些对象放入带.data.rel.ro
前缀的节中,链接器将这些节与其他节分开收集.data
。动态链接器(或者,在静态 PIE 的情况下,链接的重定位处理代码)可以mprotect
在写入该区域之后。
因此,使用 gcc (和隐式-fpie -pie
)你有:
readonlyObject
在.data.rel.ro
- 归类
nm
为“全球数据” - 在程序启动时可写以进行重定位
main
达到时只读
使用铿锵声或者gcc -fno-pie
你有:
readonlyObject
在.rodata
- 归类
nm
为“全局常数” - 即使在程序启动时也是只读的
推荐阅读
- c++ - 修改 ELF 二进制文件以扩展数组大小
- c# - 在 HTML 操作链接中创建带有图标的按钮
- python - 检查多个文件之间重复数据的最有效方法是什么?
- sql - 如何创建顺序唯一的复合主键(id_x,id_y)?
- rest - REST:输入流和内存管理
- xamarin.forms - Prism.Forms 导航 - 在模态和非模态页面之间
- ios - 推送和呈现视图控制器
- javascript - 使用绑定时找不到 javascirpt
- regex - 如果后续行短于 X 个字符,则从文本文件中删除 CR
- node.js - 需要将 typescript 编译器 `tsc` 版本升级到 3.0