如何在编译时检测 ABI?

How to detect ABI at compile time?

有没有办法在编译时检测 C/C++ 中的 ABI?我知道有 OS 和 CPU 架构的宏。 ABI 是否有类似的宏(或其他方式)?

ABI 的概念不为 C11 或 C++14 等标准规范所知。这是一个实现的事情。

您可以在 Linux 上使用 feature_test_macros(7)

您可以考虑改进您的构建过程(例如,您的 Makefile, etc...). You might run some shell script detecting features (like autoconf generated configure scripts do). Notice that some C or C++ code (e.g. header files, etc...) might be generated at build time (for examples: by bison, moc, rpcgen, swig, ...), perhaps by your own utilities or scripts. Use a good enough build automation tool (with care, GNU make and ninja 能够处理生成的 C++ 或 C 代码并管理它们的生成和依赖项)。

不要将编译与 build; the compilation commands running a compiler 混淆,它们只是构建过程的 部分

一些平台接受几个 ABI。例如。我的 Linux/Debian/Sid/x86-64 带有 Linux 4.13 内核的桌面可以 运行 x86 32 位 ELF executable, x86-64 64 bits ELF, probably some old a.out format from the 1980s, and also x32 ABI. With binfmt_misc I can add even more ABIs. See x86 psABI 用于几个 ABI 文档的列表。

顺便说一句,目前的趋势是尝试编写 portable code. Perhaps using frameworks like Qt or POCO or Glib(和许多其他)可以隐藏应用程序的 ABI 详细信息。

在某些情况下,libffi 也可能有帮助。

一般来说,一旦您了解了您的 OS 和您的架构,您几乎可以 - 大多数时候 - 推断出 ABI。

如果你真的想要你的 ABI, 那么一个可能的 Linux 具体方法可能是 运行 file(1) on the current executable. I don't recommend doing that, but you could try (using proc(5) 来获取可执行文件) :

 /// return a heap allocated string describing the ABI of current executable
 //// I don't recommend using this
 const char*getmyabi(void) {
   char mycmdname[80];
   int sz = snprintf(mycmdname, sizeof(mycmdname), 
                     "/usr/bin/file -L /proc/%d/exe",
                     getpid());
   assert (sz < (int) sizeof(mycmdname));
   FILE*f = popen(mycmdname, "r");
   if (!f) { 
      perror(mycmdname); exit(EXIT_FAILURE);
   };
   char* restr = NULL;
   size_t siz = 0;
   getline(&restr, &siz, f);
   if (pclose(f)) { perror("pclose"); exit(EXIT_FAILURE); };
   return restr;
 } // end of getmyabi
 /// the code above in untested

您可以获得如下字符串:

 "ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked,"
 " interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32,"
 "BuildID[sha1]=deca50aa4d3df4d57bacc464aa1e8790449ebf8e, stripped"

然后你需要解析它。您可能还想解析 ldd(1) or of objdump(1) on your executable, your ELF interpreter ld-linux(8) 等的输出……(或为此使用一些 ELF 解析库)。

我不知道 getmyabi 功能有多大用处。我不知道 file 给出的精确输出是什么(在各种 ABI 的所有奇怪情况下)。我让你去测试它(一定要用你系统上安装的所有 ABI 编译你的测试程序,所以 gcc -m32gcc -m64gcc -mx32,等等......);如果可能,请在某些非 x86 Linux 系统上进行测试。

如果您只需要在构建时获取 ABI,请考虑编译一些 hello-world 可执行文件,然后 运行 file(和 ldd)。有适当的构建规则(Makefile 规则)执行此操作并解析那些 fileldd 命令的输出。

(我对你的问题感到惊讶;什么样的应用程序需要知道 ABI;大多数需要 ABI 的软件都是编译器……;对精确 ABI 的强烈依赖可能是undefined behavior.)

也许给出的提示 here 可能适用于您的情况(只是盲目猜测)。

如果您正在编写一些编译器,请考虑 generating some C code in it then use some existing C compiler on that generated C code, or use a good JIT compilation library like LIBGCCJIT or LLVM。他们会处理 ABI 的特定方面(更重要的是低级优化和代码生成)。

如果您单独编写编译器并且不想使用外部工具,那么实际上您应该将自己限制在一个或几个 ABI 和平台上。 人生苦短

PS。我完全不确定 ABI 是否有确切的含义。它更像是一个规范文档,而不是某些系统(或某些可执行文件)的定义功能。 IIUC,ABI 规范确实有所发展(可能 完全 与 15 年前不一样)。