我知道为什么 GCC 默认不重新排序结构的成员,但我很少编写依赖于结构顺序的代码,那么有什么方法可以标记我的结构以自动重新排序?
4 回答
以前的 GCC 版本可以-fipa-struct-reorg
选择允许在-fwhole-program
+-combine
模式下重新排序结构。
-fipa-struct-reorg
执行结构重组优化,改变类 C 结构布局,以更好地利用空间局部性。这种转换对包含结构数组的程序有效。有两种编译模式可用:基于配置文件(使用 启用
-fprofile-generate
)或静态(使用内置启发式)。要求-fipa-type-escape
提供此转换的安全性。它仅在整个程序模式下工作,因此需要-fwhole-program
并-combine
启用。此转换考虑'cold'
的结构不受影响(请参阅 参考资料--param struct-reorg-cold-struct-ratio=value
)。
由于发行说明中的以下原因,自 GCC 4.8.x 以来已将其删除
结构重组和矩阵重组优化(命令行选项
-fipa-struct-reorg
和-fipa-matrix-reorg
)已被删除。它们并不总是能正常工作,也不能与链接时优化 (LTO) 一起工作,因此仅适用于由单个翻译单元组成的程序。
但是,您仍然可以尝试使用struct-reorg-branch
GCC SVN或github 镜像,风险自负,因为它仍在积极开发中。
您还可以使用clang-tools-extra中的clang-reorder-fields工具对字段重新排序
也可以看看
附带说明一下,Linux 内核实现了一个gcc 插件来引入一个名为randomize_layout
. 目标是在结构的定义中使用它,以使编译器随机化字段的顺序。为了安全起见, Linux 内核使用它来应对需要了解结构布局的攻击。例如,cred
结构在include/linux/cred.h中定义如下:
struct cred {
atomic_t usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
atomic_t subscribers; /* number of processes subscribed */
void *put_addr;
[...]
struct user_struct *user; /* real user ID subscription */
struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
struct group_info *group_info; /* supplementary groups for euid/fsgid */
/* RCU deletion */
union {
int non_rcu; /* Can we skip RCU deletion? */
struct rcu_head rcu; /* RCU deletion hook */
};
} __randomize_layout;
该__randomize_layout
标签在 Linux 源代码树的include/linux/compiler-gcc.h中定义为:
#define __randomize_layout __attribute__((randomize_layout))
GCC 中没有这样的选项。而且,我敢肯定,它不能以任何明智的方式介绍。关于填充优化,请查看此讨论。
我知道的唯一例外是热/冷结构字段拆分,这在某些情况下可以完成(我仍然不确定,即使在配置文件引导模式下,GCC 也可以做到,我知道 ICC 可以)。此功能不受用户控制,而是在调用图上执行,其中数据流上的这种转换的保守性是可证明的。
我认为在编译整个程序时可以重新组织/拆分 struct 的元素(lto 模式,使用 -flto 标志)。在这种情况下,您拥有程序的完整图片,并且对于不会转义的符号,应该可以重新排序它们以获得更好的缓存行为等。
在 gcc 主干中,这正在积极开发中。这是在 GNU cauldron 2015 中提出的。您可能想尝试 gcc trunk 或 struct-reorg-branch。