Arch Linux 的 GCC 预处理器指令

GCC preprocessor directives for Arch Linux

GCC(或 Clang)在为 Arch 编译时是否定义任何宏 Linux OS?

我需要检查我的软件是否限制自己在 Arch Linux 以外的任何环境下编译(这背后的原因是题外话)。我在网上找不到任何相关资源。

有谁知道如何通过 GCC 预处理器指令保证我的二进制文件只能在 Arch Linux 下编译?

当然可以

#ifdef __linux__
  ...
#endif

但这不够精确。

编辑:必须通过 C 源代码而不是任何构建系统来完成,因此,例如,完全放弃通过 CMake 来完成。

编辑 2:用户假装此行为不是问题,因为该软件已分发给选定的客户,因此,积极尝试“滥用”我们的源代码是“他们的决定”。

不,不是。即使这样做了,也不会阻止任何人在 Arch Linux 发行版上编译代码,然后 运行 在不同的 Linux.

上编译代码

如果您需要防止您的软件“来自 运行 除了 Arch Linux 之外的任何软件”,您需要插入一个 run-time 检查。虽然,老实说,我不知道该支票可能包含什么,因为 linux 发行版不是单一产品。实际检查可能与您施加限制的原因有关。

您可以使用

列出预定义的预处理器宏
gcc -dM -E - /dev/null
clang -dM -E - /dev/null

None 表示编译器 运行 在哪个操作系统下。所以不仅你无法分辨程序是否在ArchLinux下编译,你甚至无法分辨程序是否在Linux下编译。宏 __linux__ 和朋友表示程序正在编译 for Linux。它们在 cross-compiling 从另一个系统到 Linux 时被定义,而当 cross-compiling 从 Linux 到另一个系统时没有定义。

您可以通过为系统 headers 指定绝对路径并依赖 non-portable headers(例如 /usr/include/bits/foo.h)来人为地使您的程序更难编译。如果不修改源代码,这会使 cross-compilation 或除 Linux 之外的任何编译几乎不可能。但是,大多数 Linux 发行版都将 headers 安装在同一位置,因此您不太可能查明具体的发行版。

你很有可能 asking the wrong question。与其询问 如何 将编译限制为 Arch Linux,不如从 为什么 开始你想将编译限制为 Arch Linux .如果答案是“因为生成的程序在另一个发行版下不是我想要的”,那么从那里开始并确保差异导致编译错误而不是不正确的执行。如果“为什么”的答案是别的,那么您可能正在寻找解决社会问题的技术解决方案,而这很少有好的结果。

Does GCC (or alternatively Clang) defines any macro when it is compiled for the Arch Linux OS?

没有。因为在二进制级别上没有什么是 Arch Linux 固有的。对于它的价值,在编译时 you/the 编译器唯一需要关心的是目标体系结构(即它将 运行 使用哪种 CPU),数据类型大小和对齐方式和函数调用约定。

然后,当需要将已编译的翻译单元对象 link 转换为最终的二进制可执行文件时,周围的 运行time 库也值得关注。如果不采取特别的预防措施,您实际上是将自己锁定在由 linker.

提取的特定品牌的 运行time 库中(glibc 与 musl;libstdc++ 与 libc++)

可以通过静态 linking 轻松回避后面的问题,但这限制了程序可用的系统和中级 API 的范围。例如,在 Linux 上,一个纯粹天真的静态 linked 程序将无法使用像 OpenGL-3.x 或 Vulkan 这样的图形加速 API,因为它们依赖于 GPU 驱动程序的加载组件进入过程。但是,您仍然可以使用 X11 和间接 GLX OpenGL,因为它们使用通过套接字的有线协议工作,这些套接字是使用对内核的直接系统调用实现的。

并且这些内核系统调用 在二进制级别上对于每个发行版的每个 Linux 内核完全相同在那里。尽管在内核内部在重新定义接口方面有很多回旋余地,但在涉及到用户空间(即常规程序)的接口时,有一条神圣的、教条的、铁定的规则 你永远不会破坏用户域! 内核开发人员有意无意地违反了这条规则,Linus Torvalds 在他的 in-/famous 咆哮中公开抨击。

最重要的是,不存在“Linux 二进制级别的分发特定标识符”这样的东西。归根结底,Linux 分布就是:东西的分布。这意味着某人或更多人决定了一组构成工作 Linux 系统的文件,以某种方式将其包装起来并为其命名。而已。 “Arch”Linux 除了被称为“Arch”并且(暂时)依赖于 pacman 包管理器之外,没有什么固有的特定于“Arch”。而已。关于“Arch”或任何其他 Linux 发行版的其他一切都只是偶然事件。

如果你真的想根据二进制兼容性将不同的 Linux 分布分类到某些容器中,那么你必须将

的组合分类
  • 最少需要一组受支持的系统调用。这转化为所需的最低内核版本。

  • 正在使用什么 libc 变体;以及可能是哪个版本,尽管完全有可能 link 反对一组最低限度支持的功能,但它几乎“永远”存在。

  • 分发决定了 C++ 标准库的哪个变体。这实际上也会造成看起来纯 C 的程序,因为某些系统级库(*cough* Mesa *cough*)将在内部拉取许多 C++ 基础设施(甚至编译器),也引发了其他“有趣”的问题¹

I need to check that my software restricts itself from running under anything but Arch Linux (the reason behind this is off-topic). I couldn't find any relevant resources on the internet.

您无法在 Internet 上找到资源,因为在二进制级别上没有任何特定内容可以使“Arch”成为 Arch。对于现在的价值,此刻我可以创建 Arch 的一个分支,更改其对默认 XDG 框架的选择——这样默认情况下用户目录会填充名为 leechflicksbeats, pics – 并称之为“l33tz”Linux。出于所有意图和目的,它不再是 Arch。它的行为确实与默认的 Arch 行为有很大不同,如果您依赖 任何 特定的东西,这也是您所关注的,并且是最微小的。

您的雇主似乎不了解 Linux 是什么或不同分布之间的区别。

提示:这不是二进制兼容性。事实上,只要你停留在无聊的旧领域 glibc + libstdc++ Linux彼此兼容。除了 libc.solibdl.sold-linux[-${arch}].so 之外,它们放置库的位置可能略有不同,但通常总是可以在 /lib 下找到这两个库。一旦 ld-linux[-${arch}].solibdl.so 接管(这意味着拉入所有在 运行 时间加载的库),所有共享对象和库所在位置的细节都被动态抽象掉了link呃。


1:就像只有在全局构造函数被执行并且 libstdc++ 决定它想要成为单线程之后才成为多线程,因为 libpthread 没有被 link 编辑到一个没有创建的程序中单独创建一个线程。那是我发现的一个非常奇怪的错误,但 yshui 终于明白了 https://gitlab.freedesktop.org/mesa/mesa/-/issues/3199