微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么静态内联数据成员不会出现在 Macos 上的 .bss 部分?

如何解决为什么静态内联数据成员不会出现在 Macos 上的 .bss 部分?

在 Macos 上试用 snmalloc 我想知道为什么 all the created binaries are >256MiB

事实证明,零初始化的 static inline 数据成员在 Mac OS X 上以一种奇怪的方式降低,在 ARM64 和 x86_64 上。即使是这个简单的测试也会产生巨大的二进制文件

容器.h

#pragma once
#include <cstdint>

class Container {
    public:
        inline static uint8_t inner[256000000];
};

main.cc

#include "container.h"

int main() {
    return Container::inner[0];
}

编译如下:

$ ~/clang+llvm-12.0.0-x86_64-apple-darwin/bin/clang -O3 -std=c++17 main.cc --target=x86_64-apple-darwin -c; ls -l main.o
-rw-r--r--  1 hans  staff  256000744 Jun 21 16:29 main.o

开源 clang 和 Apple clang 一样。 gcc 的行为类似。

在 Linux(使用 clang 或 gcc 编译)上,它包含在 .bss 部分中,因此不占用任何空间。

为什么 Macos 会出现这种情况?这是错误还是预期行为?

解决方法

我会继续尝试回答这个问题,尽管我会第一个承认,在您遇到一堵墙之前,您只能给出一个答案,“因为有人做出了决定并且你永远被它困住了。”

所有这些的主键以 MacOS 的 Mach-O 运行时规范的形式出现,该规范将 .bss 部分定义为用于:

未初始化的静态变量(例如,static int i;)。

您可以在 10.3 版的此 archived version 中阅读相关信息,但您也可以在其他 Mach-O 参考资料中找到相同的信息。

这里要注意的重要一点是,bss 的使用指的是“私有”符号。换句话说,这指的是 static 关键字的 C 风格使用,该关键字保证是翻译单元的本地化。

当您将 C++17 成员变量声明为 static inline 时,尽管使用了反常重载的 static 关键字,但您已经创建了一个全局对象,其中保证只有成为程序中的一个实例。换句话说,使用此声明编译的每个翻译单元都将实例化它,并且期望链接器通过选择其中之一将它们“合并”为单个实例。这显然与 C 风格的“未初始化的静态变量”截然不同。

像clang这样的MacOS主机编译器通过将符号声明为weak DATA来实现这一点,例如类似于默认构造函数的声明方式(尽管那些当然会在TEXT中) .

为了说明这一点,请注意,完全不使用 C++17 也可以获得相同的效果。例如编译这些示例集并查看程序集输出:

static uint8_t stuff[256000000]; // <- goes into .bss

int main() {
    return (int)reinterpret_cast<uint64_t>(&stuff[0]);
}

请注意,在这种情况下,我必须在这里执行 &stuff 以确保编译器不会完全优化掉 stuff

现在试试这个:

uint8_t stuff[256000000]; // <-- goes into __DATA,__common

int main() {
    return (int)reinterpret_cast<uint64_t>(&stuff[0]);
}

越来越近了。请注意,stuff 不会像您在 linux 平台上看到的那样放入 .bss。再次根据 Mach-O 运行时规范,common 部分用于:

未初始化的导入符号定义(例如,int i;)位于全局范围内(在函数声明之外)。”

现在试试这个:

__attribute__((weak)) uint8_t stuff[256000000]; // <-- in DATA,__data

int main() {
    return (int)reinterpret_cast<uint64_t>(&stuff[0]);
}

这正是定义 static inline C++17 成员变量的方式。在引擎盖下,clang 已将此符号指定为“合并”数据,在 x86 上它只是变成标准数据。如果你真的想深入了解香肠工厂,你实际上可以在 llvm SelectSectionForGlobal 函数中看到。

   if (GO->isWeakForLinker()) {
     if (Kind.isReadOnly())
       return ConstTextCoalSection;
     if (Kind.isReadOnlyWithRel())
       return ConstDataCoalSection;
     return DataCoalSection;
   }

并且 DataCoalSection 被相应地定义为 here 与除 Power PC 之外的所有内容上的普通数据部分相同。

因此,从我的角度来看,鉴于 Mach-O 运行时的可用规范,您所看到的行为正如我所期望的那样。

,

尝试为类实例化一个对象并从该对象调用成员。

Container obj;
cout << obj.inner[0];

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?