已归档 | 在 AIX 上使用 GNU C/C++ 编译器

462 阅读17分钟

已归档 | 在 AIX 上使用 GNU C/C++ 编译器

常见问题与解决方案

作者:Anita Govindjee, David Edelsohn, Ramesh Chitor

最后更新:2008年8月12日 | 发布时间:2005年1月12日


已归档内容

归档日期:2019-08-13

此内容已不再更新维护。文章内容按原样展示。鉴于技术的快速迭代,某些内容、步骤或插图可能已经发生变化。

GCC 基础

GCC 编译器属于自由软件基金会 GNU 项目的一部分。GCC 是基于开源环境开发的,所有的 GNU 工具均是如此,它支持包括 AIX 在内的许多平台。GCC 全称是 GNU 编译器套装(GNU Compiler Collection),它支持多种编程语言,如 C、C++、Objective-C、Fortran 和 Java。在本文中,我们将讨论 AIX 版本 5.1、5.2 和 5.3 上的 GCC 3.3.4 版本和 3.4.x 版本。

GCC 编译器和 XL C/C++ 编译器对比

XL C/C++ for VIX V7.0 企业版是 VisualAge® C++ for VIX V6.0 专业版的后续版本。在本节中,我们将讨论为什么您想要在 AIX 上使用 GCC 编译器而不是 XL C/C++ 编译器。XL C/C++ 编译器对最新的国际和行业编程标准有着很好的支持。它带有用于浮点数除法的新的内置函数、新的编译器指示以及额外的新编译器选项等。IBM 的 XL C/C++ for AIX V7.0 增强版的一个关键特性是它和 GCC 编译器进一步兼容。为了帮助移植那些为 GCC 编写的代码,XL C/C++ 支持和 GNU C/C++ 相关的功能子集。重要的是,XL C/C++ 编译器海为包括 POWER5 在内的 PowerPC 做了优化。

但是在有些情况下,使用 GCC 编译器可能更具吸引力。那些使用标准 API 编写并用 GNU gcc 或 g++ 编译器编译的应用程序,会更容易实现跨多个平台移植。GCC有多个语言前端,便于解析多种语言。GCC 还是可移植的,在当今大多数的平台都能运行,它还支持大多数商业 64 位 CPU。GCC 还是一个可以交叉编译任何程序的本地编译器,可为与 GCC 本身所使用系统不同的系统生成可执行文件。这就可以为那些不能运行编译器的嵌入式系统编译软件了。GCC 是用 C 编写的,非常注重可移植性,并且可以编译自身,因此可以轻松适应新系统。总之,GCC 使您可以自由地增强现有的 GCC 以及别人开发的其他 GNU 软件。

当开发人员尝试让他们的应用程序在多个平台上运行时,在不同平台上使用不同的编译器可能会导致许多令人头痛的问题。相反,如果您使用 GCC 编译器,它在多个平台上都受支持,就可以帮您减少移植的麻烦。GCC 通过提供一个跨平台的通用编译器环境,来让您的工作更加轻松。G++ 现在也更加接近于 ISO/ANSI C++ 标准了。

值得注意的是 AIX 系统默认的 make 命令不支持 GCC make 文件。如果您想在 AIX 上使用 GCC 编译器,您需要有 GNU make 文件。您可以打印下 make v 命令看编译 GCC 都需要什么。

AIX 5L 二进制兼容性

IBM 在 AIX 5.1、5.2 和 5.3 版本的操作系统之间提供了二进制兼容支持。因此只要运行在AIX 5.1 和 5.2 上的软件遵循 IBM AIX 5L 二进制兼容性 里列出的声明,也可以运行在 5.3版本的系统上。话虽如此,但独立软件开发商(ISV)在添加对新操作系统版本支持这一过程上有着很大差异。许多顶级 ISV 通过某种形式的测试来运行他们的应用程序。大多数情况下,在添加支持之前,他们将通过最终测试程序的子集来运行这些程序。然而,许多其他 ISV 会查看我们的二进制兼容性详细信息,并根据他们的应用程序是否符合我们的声明来添加支持。不管怎么着,都不需要重新编译应用程序来安装到 AIX 5.3 上。

在 AIX 系统上安装 GCC

如果您有安装到 AIX 系统可用的二进制版本,在 AIX 上安装 GCC 就会很简单。二进制文件可以从以下站点通过 ftp 传输到您的 AIX 系统,如 参考资料 部分所示:

  • 公牛 AIX 系统免费软件(Bull AIX Freeware)
  • 哈德逊谷社区学院开源软件(Hudson Valley Community College Open Source Software)
  • AIX 5L 和 6 开源包(AIX 5L and 6 Open Source packages)
  • 适用于 Linux 应用程序的 IBM AIX Toolbox(IBM AIX Toolbox for Linux applications)

下载对应的二进制格式文件后,执行 chmod 将下载的文件改为可执行模式,然后运行 SMIT 进行安装。 GCC 3.3 版中添加了对 AIX 5.1和 5.2 的支持。

IBM AIX Toolbox for Linux 应用程序网站提供了 GCC 3.3.2 二进制文件,可用于 AIX 5.1 和 5.2。 公牛 AIX 免费软件(Bull AIX Freeware)站点还提供了适用于 AIX 5.2 的 GCC 3.3.2 版二进制文件。

如果您需要更新版本的GCC,您首先得在您的系统上安装一个可用的 GCC 二进制文件。安装之后,您就可以用它在 AIX 系统上编译更新版的 GCC 了。请参阅参考资料来查找 GCC 安装教程以及用于特定主机/目标的 GCC 的安装教程。一定要特别仔细地按照特定平台的说明来进行操作。在开始构建和安装过程之前,请仔细阅读说明。

您可能会发现 GCC 构建报告有助于确保您成功安装 GCC 4.3.x(参见 参考资料)。 相应地,也有 AIX 5.3 的构建报告。

请确保您安装的 GCC 版本与系统上安装的 AIX 发行版是对应的。GCC 会安装一些头文件的私有副本,这些头文件必须与 AIX 系统头文件正确集成才能使 GCC 正常运行,否则运行编译器就会产生有关头文件错误的信息。如果头文件不匹配,则应重新构建这些头文件。 您可以删除头文件缓存以从旧版本的源码构建新的 GCC,但不能在没有头文件缓存的情况下运行 GCC。

GCC 编译器选项

GCC 上提供了多种编译器选项,从代码优化、设置或关闭 ISO/ANSI 标准代码编译到调试选项、模板代码编译选项。 GCC 还提供了一些特定于 pSeries(以前称为 RS/6000)和 PowerPC 平台的编译器选项。

有关 POWER 和 PowerPC 目标可用选项的完整说明,请访问 gcc.gnu.org/onlinedocs/… 在这里,我们只介绍一些我们认为最常用的选项。

在设置处理器类型的选项中,例如 -mcpu-mtune,最好使用 GCC 提供的默认值。 在 AIX 4.3 和 AIX 5.1 上,默认值为 -mcpu=common — 此代码适用于 pSeries (RS/6000) 系列处理器的所有成员。 在 AIX 5.2 及更高版本上,默认值为 -mcpu=powerpc -- 此代码将适用于 pSeries PowerPC 处理器。 GCC 假定与 AIX 版本相关联的最新处理器类型作为用于调度目的的处理器模型。

您应该使用 -mcpu=power-mcpu=power2-mcpu=powerpc-mcpu=power4 等来针对特定处理器或处理器类别进行优化。 不要使用-mpower-mpowerpc-mpower2-mpowerpc64 选项。 尽管它们可以配置,但它们并非面向最终用户。 单独使用 -mpower2 或其他选项可能会导致不可预测的结果,因为编译器可能不会处于该目标处理器的自洽状态。

在 64 位模式下编译应用程序时,应使用选项 -maix64,它启用 64 位 AIX ABI,例如:64 位指针、64 位长整型以及支持它们所需的基础结构。 指定-maix64意味着-mpowerpc64-mpowerpc,而-maix32会禁用64位 ABI 并意味着 -mno-powerpc64。 GCC 默认为 -maix32。

如果您看到一条链接器错误消息,提示可用 TOC(Table of Contents)空间已溢出,您可以使用 -mminimal-toc 减少使用的 TOC 空间。 默认情况下,GCC 使用 -mfull-toc 为程序中的每个唯一的非自动变量引用分配至少一个 TOC 条目。 GCC 还在 TOC 中放置了浮点常量。 但是,TOC 中只有 16,384 个条目可用,可能会导致 TOC 溢出。 通过指定-mminimal-toc GCC 只会为每个函数创建一个 TOC 条目。

-pthread 会编译您的应用程序代码并将其与 POSIX 线程库链接,类似于您在 VisualAge(或 XL)C/C++ 中做的。 使用 VisualAge(或 XL)C/C++,您还可以使用 xlc_rxlC_r 编译和链接来获得线程支持,但 GCC 不存在此选项。 因此,线程代码应该与 GCC 的 -pthread 链接。 -pthread 选项会同时为预处理器和链接器设置标志。

-g 选项会生成调试信息,默认值 -gxcoff+ 为 2 级别。调试信息可能包含一些用于 GNU 调试器 (GDB) 的扩展。 要生成与 AIX dbx 更兼容的调试信息,可以使用 -gxcoff 选项。

AIX 的 GCC 不完全支持 -msoft-float。 它会禁用在 GCC 中使用浮点寄存器,但 GCC 支持库和 AIX 库中会继续使用浮点寄存器。

-mlittle-endian-mcall-linux 在 AIX 配置中无效。 文档提到了针对所有 GCC “rs6000”目标的所有选项。 在每个配置中并非所有的选项都可用。

要将选项直接传递给本机 AIX 链接器,使用 -Wl, <linker option>

G++ 和 GCC 编译器选项

可以在 gcc.gnu.org/onlinedocs/… 找到 g++ 特定编译器选项的描述。 这些选项在 AIX 上是相同的。

同样,可以在[gcc.gnu.org/onlinedocs/…gcc.gnu.org/onlinedocs/… Dialect Options)找到 gcc 特定编译器选项的完整描述。 这些选项在 AIX 上也与其他 UNIX 系统相同。

优化选项列表可以在 gcc.gnu.org/onlinedocs/… 找到。

最常见的优化选项是-O2,它可以进行许多优化以提高生成的可执行文件或库的性能。 -O3 会启用更多优化,可以以额外的编译时间为代价来提高性能。 -Os 优化选项会提高性能以外,也会尝试降低代码大小。

对计算密集型科学应用有用的其他优化选项包括-funroll-loops-ffast-math-ffast-math 允许编译器以牺牲数学函数的精确 IEEE 和 ISO 一致性为代价来提高性能。

AIX 与 System V 系统上的共享库对比

首先,让我们看看 AIX 和 System V 系统在共享库方面的区别。 接着我们会讨论如何使用 GCC 在 AIX 系统上创建共享库。

AIX 和 System V 对共享对象有不同的理解。 AIX 通常将共享对象视为完全链接和解析的实体,其中符号引用在链接时解析,不能在加载时重新绑定。 System V 认为它们与普通对象文件非常相似,其中所有全局符号的解析都由链接器在运行时执行。 但是,AIX 确实具有执行运行时链接 (RTL) 的能力,因此符号可能会在加载前保持未定义状态,此时会在所有加载的模块中执行搜索以找到必要的定义。

AIX 中的共享模块可以是一个对象文件或者包含共享模块和/或普通对象文件的归档文件。而在 System V 中,共享库文件只能是使用特殊选项创建的普通文件。

在 AIX 中,通常链接器的输入共享对象仅在它们的符号被引用时才会列为输出文件的依赖项。AIX 还用导出文件来显式地导出符号。在 System V 中,命令行中列出的所有共享库的名称都会保存在输出文件中,以便在加载时可能用得到。但是,从 AIX 4.2 开始及后续所有可用的 AIX 发行版里(4.3,5.1 和 5.2),-brtl 选项可以实现所有在命令行列出的共享对象(归档成员除外)作为输出文件的依赖项。有关更多信息,请参阅 AIX 链接和加载机制 (PDF 184KB)。

使用 GCC, gcc -shared 可以创建一个共享库。 链接器会自动导出一些符号,但您可以使用 AIX 的 ld -bexpall 导出更多符号。 带下划线的符号仍然不会导出。 在 2 的 AIX 5.2 或 AIX 5.1 上,可以使用新的 -bexpfull 链接选项,该选项将导出所有符号并且不会跳过以下划线开头的符号(如 C++ mangled names)。 为了完全控制,最好的做法是创建一个导出文件。 如果您不熟悉 AIX 的导出文件,请参阅 AIX 链接和加载机制 (PDF 184KB)。

gcc -shared 会创建一个 AIX 风格、紧密绑定的共享对象,如上面 AIX 与 SystemV 部分中概述的那样。 gcc -shared 使用 -bM:SRE -bnoentry 调用 AIX 链接器。 因此,您不需要重复这两个 AIX 选项。

使用-brtl,AIX 链接器将查找具有.a.so 扩展名的库,例如libfoo.alibfoo.so。 如果没有 -brtl,AIX 链接器只会查找 libfoo.a。 您可以简单地通过归档共享对象或者将共享对象重命名为“libfoo.a”来创建“libfoo.a”——AIX 不关心这些,只要文件后缀是“.a”就行。 要使用 AIX 运行时链接,您应该使用 gcc -shared -Wl,-G 创建共享对象,并通过将 -Wl-brtl 选项添加到链接行来使用库创建可执行文件。 从技术上讲,您可以不使用 -shared 选项,但这不会造成任何伤害或者减少混淆。

如果你想用 GCC 创建一个共享对象,你只需要使用 gcc -shared 或者加上 -bexpfull 选项,也可以使用 -Wl-bE:<export filename> 引用的导出文件:<export filename>.exp。 就这么简单。

在 AIX 上使用 GCC/G++ 可能遇到的问题

下面是一些您在 AIX 上使用 GCC/G++ 可能遇到的问题。对于每一项,我们先回描述这个问题,然后展示这个问题的解决办法。

  • 您不能在 AIX 上使用 GNU 链接器,因为这会导致错误链接的二进制文件。 默认情况下,在 AIX 上使用 GCC 时使用 AIX 链接器。 您应该只使用默认的 AIX 链接器。

  • 用 -pthread 编译链接时,应该在库搜索路径的开头包含-L/usr/lib/threads。 检查 dump -Hv 的输出,导入文件字符串,entry 0,查看路径是什么。 线程子目录应始终位于 /usr/lib 和 /lib 之前。

  • 运行 gcc -o foo ... -L/path -lmylib 不起作用。库文件是无法打开的,因为它查找的是静态库 libmylib.a 而不是 libmylib.so。 如果扩展名 .so 重命名 .a,它就可以正常工作了——它可以编译并运行。 AIX 上的归档库和共享库都可以具有 .a 扩展名。 这就解释了为什么您不能与 .so 链接以及为什么它可以在名称更改为 .a 的情况下使用。

  • 使用 C++ 共享库和异常处理运行 64 位 C++ 应用程序时,应用程序崩溃。 GCC 3.4.3 修复了跨共享库的 C++ 异常处理在 64 位模式下不能正确运行的问题。

  • 将应用程序与目标文件存档(普通库,而不是共享库)链接会产生有关未解析符号的错误消息。 如果库包含的对象文件引用了开发人员认为链接器会忽略的库外部符号,则可能会发生这种情况。

    GCC 为系统链接器提供了一个包装器,用于扫描所有目标文件和非共享库的构造函数和析构函数。 这是在链接器有机会跳过库中不需要的目标文件之前完成的,因为函数或数据可能只在构造函数或析构函数中引用。 扫描可能会发现应用程序不正常引用的构造函数和析构函数,需要额外的符号定义来满足链接。 替代方法将会丢弃应用程序所需的构造函数和析构函数,因为库中的某些目标文件似乎不需要并会被省略,但实际上提供了必需的构造函数和析构函数。 这种遗漏会导致应用程序中断。

  • 尝试在 AIX 上编译 GCC 时,最终会产生汇编器错误消息。 要解决此问题,请确保您使用的不是旧版本的 GNU 汇编器,它不了解某些 PowerPC 助记符。 最好按照 GNU GCC 网站上的 AIX 安装说明中所说的那样,使用 AIX 汇编器。

  • 有时您可能会在链接时收到此错误消息:

    ld fatal: failed to write symbol name symbol_name in strings table for file filename
    

    此错误很可能表示磁盘已满或 ULIMIT 环境变量不允许文件达到所需的大小。

  • G++ 不会以与 VisualAge(或 XL)C++ 相同的方式进行名称修改(name mangling)。 这意味着用一个编译器编译的目标文件不能与另一个编译器的一起使用。

  • GNU 汇编器 (GAS) 不支持 PIC。 要生成 PIC 代码,您必须使用其他一些汇编程序,例如本机 AIX 汇编程序 /bin/as

  • 在 AIX 上,编译以下形式的代码:

    extern int foo;
    ... foo ...
    static int foo;
    
  • 将导致链接器报告未定义的符号 foo。 尽管此行为与大多数其他系统不同,但它不是错误,因为在 ANSI C 中将外部变量重新定义为静态变量是未定义的

  • 在 GCC 3.4 之前的 GCC 版本中,不会按值传递所有结构。 GCC 3.4 现在对结构参数传递有更好的 ABI 支持。 虽然此更改使 GCC 更符合 AIX ABI,但这些更改可能会导致与早期版本编译的代码不兼容。 有关更多信息,请参阅 gcc.gnu.org/gcc-3.4/pow…

  • 当为特定版本的 AIX 构建 GCC 时,它会生成一些特定于该版本操作系统的文件及其头文件。 在较新版本的操作系统上使用该 GCC 构建时,可能会出现问题,即使只是在较新的操作系统上引导编译器。 一个例子是使用为 AIX 5.1(或更早版本)编译的 GCC 在 AIX 5.2 上构建 GCC 时。 AIX 5.2 添加了对 atoll() 函数的支持,但为 AIX 5.1 构建的 GCC 包含 stdlib.h 的缓存副本,该副本不包含该函数的原型。 非原型函数的返回值默认为“int”而不是“long long”,导致 GCC 错误地转换某些字符串。 要在 AIX 5.2 上引导 GCC,需要删除 GCC 缓存中“固定”的 stdlib.h 头文件,以便使用 AIX 5.2 头文件。 旧版 GCC 可以使用系统头文件来引导编译器。

一个例子

这是一个使用 pthreads ,每个线程有足够大的堆栈大小,使用 GCC 编译的程序示例和相应的 makefile。

数组分配在堆栈上,而不是在数据段中。 问题是每个线程的堆栈大小。 您需要使用适当的 pthread 函数来增加默认线程堆栈大小。 如果没有下面的“#ifdef _AIX”代码,此代码将进行核心转储。

$ cat test.c
#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 3
#define SIZE 100000
void *PrintHello(void *threadid)
{ 
  double array[SIZE];
  int i;
  for (i = 0; i < SIZE; i++)
  array[i] = (double) i;
  pthread_exit(NULL);
}
int main() {
  pthread_attr_t tattr;
  pthread_t threads[NUM_THREADS];
  int rc, t;
  if (rc = pthread_attr_init(&tattr)) {
  	printf"ERROR: rc from pthread_attr_init() is %d\n", rc);
    exit(-1);
  }
#ifdef _AIX
  if(rc = pthread_attr_setstacksize(&tattr, PTHREAD_STACK_MIN + SIZE *  sizeof(double))) {
    printf("ERROR: rc from pthread_attr_setstacksize() is %d\n", rc);
    Exit(-1);
  }
#endif
  for (t = 0; t < NUM_THREADS; t++) {
    printf("Creating thread %d\n", t);
    if (rc = pthread_create(&threads[t], &tattr, PrintHello, (void *)t)) {  
      printf("ERROR: rc from pthread_create() is %d\n", rc); exit(-1);
    }
  }
  pthread_attr_destroy(&tattr); pthread_exit(NULL);
}
$ cat Makefile
host := $(shell uname)
CC = gcc
ifeq (,$(findstring CYGWIN,$(host))) # not Cygwin
     LDFLAGS += -lpthread
endif
PROGRAM = test
OBJECTS = test.o
all: $(PROGRAM) $(PROGRAM): $(OBJECTS)
clean: rm -f $(PROGRAM) $(OBJECTS) core tags