最可靠方法是利用链接器生成的边界符号(如__start_rodata和__stop_rodata)计算地址差;Windows需解析PE头中.rdata节的VirtualSize;跨平台无统一方案。

ROData 区段在链接器脚本里通常叫 .rodata,不是靠 C++ 语言本身能直接“获取”的
标准 C++ 没有暴露内存布局或段大小的接口。所谓“获取 ROData 大小”,本质是读取 ELF(Linux/macOS)或 PE(Windows)二进制文件中 .rodata 段的尺寸,或者利用链接器生成的符号来推算——这属于构建时/加载时的元信息,不是运行时语言特性。
常见错误是试图用 sizeof 或遍历全局 const 变量,但这些完全不可靠:编译器可能内联、合并、甚至把字符串字面量放进 .text;sizeof 只作用于类型或对象,不反映段边界。
最可靠方法:用链接器生成边界符号(GNU ld / LLD / macOS ld64 支持)
让链接器在 .rodata 起始和结束处注入两个特殊符号,比如 __start_rodata 和 __stop_rodata,然后在代码里取地址相减。这是 Linux 内核、musl、Zephyr 等项目实际采用的方式。
- 在链接脚本里显式定义(或使用默认支持的符号名),例如:
_start_rodata = .; *(.rodata .rodata.*); _end_rodata = .;
- 或更通用的做法(无需自定义链接脚本):GCC/Clang 默认识别
__start_<i>section</i>和__stop_<i>section</i>,只要确保段名拼写一致(注意大小写和点号) - 在 C++ 代码中声明外部符号并计算:
extern "C" char __start_rodata, __stop_rodata; size_t rodata_size = &__stop_rodata - &__start_rodata;
- 必须用
extern "C"声明,避免 C++ name mangling;符号地址是char*类型,直接相减得字节数
Windows 上用 IMAGE_SECTION_HEADER 解析 PE 文件
Windows 没有等效的 linker symbol 机制,需在运行时打开当前模块(EXE/DLL),解析 PE 头,定位 .rdata 段(Windows 对应 ROData 的默认节名)。
立即学习“C++免费学习笔记(深入)”;
- 调用
GetModuleHandle(nullptr)获取当前模块基址 - 强制转换为
PIMAGE_DOS_HEADER→PIMAGE_NT_HEADERS→ 遍历IMAGE_SECTION_HEADER数组 - 查找
Name字段为".rdata\0\0"(8 字节,含填充)的节 - 其
SizeOfRawData是磁盘对齐后的大小,Misc.VirtualSize是内存中实际占用(更准确) - 注意:ASLR 和 DEP 可能影响地址有效性,但节尺寸本身是静态的,不受运行时重定位影响
用 objdump 或 readelf 查看验证,别信 IDE 显示的“只读数据”估算值
IDE(如 VS、CLion)或编译器报告的“ROData size”往往是粗略统计,不含 padding、对齐填充、或被合并到其他段的常量(比如某些 const int 可能进 .data)。真正可信的只有二进制文件本身的段头信息。
- Linux/macOS:
readelf -S your_program | grep rodata或objdump -h your_program | grep rodata - Windows:
dumpbin /headers your_program.exe | findstr ".rdata" - 输出中的
Size列(readelf)或size字段(dumpbin)就是真实字节数 - 若找不到
.rodata或.rdata,说明编译器未生成独立段(例如全优化下常量被折叠进指令),此时大小为 0
真正麻烦的是跨平台一致性——Linux 用符号法,Windows 必须解析 PE,而 macOS 的 Mach-O 需用 getsegmentbyname + getsectionbyname;没有一行 C++ 代码能通吃。别漏掉对齐边界:即使你算出符号差是 1234 字节,实际段在内存中可能按 4KB 对齐,但这不影响“逻辑 ROData 大小”的定义。


















