特定架构说明 —— AMD64/EM64T
独立代码问题
Gentoo 策略要求所有共享对象都必须使用CFLAGS
中的-fPIC
进行编译。由于这只是一条规则,因此你可以在某些架构门上打破它。你可能永远不会注意到它。在 AMD64 上,这是必须的,如果共享对象不是在不支持位置无关代码的情况下构建的,则构建过程会因以下错误消息而失败:
foo.o: relocation R_X86_64_32 can not be used when making a shared
object; recompile with -fPIC
如何解决-fPIC 问题
有几种方法可以对共享库强制执行-fPIC
,每种方法各有利弊。
sed
ing Makefile 文件
有时候,一个简单的sed
命令就可以修复它,但是,这些语句通常不易读,并且在上游更改文件时可能会失败。请确认你仅更改共享对象的CFLAGS
,而不更改整个包。
修补Makefile.in
/configure
这更具可读性,并且更易于向上游发送。
如何不解决-fPIC
问题
不要修补Makefile
本身,因为它通常是由configure
脚本生成的,并且可能相差很大,因此修补可能会失败。而且,这根本对上游没有帮助。
另一个不好的主意是(ab)使用 flag-o-matic.eclass 中的append-flags
函数。不应将-fPIC
应用于所有对象。它仅应应用于共享对象。
AMD64 上的 Multilib
当前的 AMD64 处理器能够在 64 位内核上本地运行 32 位代码。因此,你可以在 amd64 环境中运行为 x86 编译的程序。但是,需要将 32 位应用程序链接到 32 位库。混合它们将不起作用。因此,对库进行排序,通常将 32 位库分别转到/lib32/usr/lib32
,将 64 位库通常转到/lib64
或/usr/lib64
。在理想的世界中,你无需继续阅读。不幸的是,事实并非如此,因此要复杂一些。
Multilib 工具链
GCC
要生成 32 位代码,我们需要具有 multilib 功能的 GCC。在其他体系结构上,此功能通过 USE 标志multilib
启用。具有 2005.0 之前版本的配置文件的 amd64 也是如此。从 2005.0 开始,你必须通过选择配置文件来选择是否需要多库支持。如果不想,请选择2005.0/no-multilib
,其他所有配置文件都将multilib
USE 标志屏蔽掉,你将被迫使用。使用这些配置文件,每当你在命令行中添加-m32
时,GCC 都会生成 x86 代码。添加-m64
或忽略任何位宽选项将默认生成 64 位代码。
glibc
如果您选择了 multilib 配置文件,则 glibc 将被构建两次,一次是 64 位,一次是 32 位。这是因为几乎每个应用程序都针对 glibc 进行链接。要了解如何在 ebuild 中完成此操作,请阅读ABI 变量。
emul-linux-x86-*
软件包
如上所述,必须将 32 位应用程序链接到 32 位库。为此,我们将最常用的库组合在一起,并将其粘贴到所谓的emul-linux-x86
软件包中,这些软件包位于app-emulation
类中。在此处可以找到所有emul-linux-x86
软件包的当前列表。
这些软件包仅提供预编译的库。当前,归档文件是手动组装的,这是保持软件包尽可能整洁的主要原因。不要包括不常用的库。
/usr/share
,无论如何都与架构无关)中发生冲突。
Libdir
链接
当前,我们提供了多个配置文件,每个配置文件都有自己的 libdir
配置组合。
Profile | lib32 | lib | lib64 |
---|---|---|---|
2004.3 | *l->emul* | d64 | *l->lib* |
2004.3/lib64 | *l->emul* | *l->64* | d64 |
>=2005.0 | d32 | *l->64* | d64 |
>=2005.0/no-multilib | d32 | *l->64* | d64 |
>=2005.0/no-symlink | d32 | d | d64 |
>=2005.0/no-symlink/no-lib32 | inexistant | d32 | d64 |
d
包含混合位对象的目录
dXX
包含 XXbit 对象的目录
l->foo
链接至 foo
为了始终获得正确的路径,应该使用multilib.eclass
中的$(get_libdir)
函数。在所有架构上,它将始终返回正确的目录。当然,它也处理ABI
变量。
multilib-strict
函数
许多 Makefile 假定它们的库应转到/usr/lib
或 $(prefix)/lib
。如果/usr/lib
不是/usr/lib64
的符号链接,则此假设可能会导致严重混乱。要查找错误的软件包,我们有一个名为multilib-strict的移植功能。这将防止 emerge 将 64 位库放入除(/usr)/lib64
之外的任何其他库中。
multilib-strict
当前不检查 perl5,gcc,gcc-lib 和 eclipse-3,此行为由make.profile
中的MULTILIB_STRICT_EXEMPT
变量控制。
如何正确修复 ebuild
在大多数情况下,使用multilib.eclass
中的$(get_libdir)
函数即可 :
inherit multilib
src_compile() {
econf \
--libdir=/usr/$(get_libdir)
emake || die
}
src_install() {
emake DESTDIR="${D}" install || die
}
一些软件包提供了非常糟糕的 Makefile 文件,这些文件使用了/usr/lib
硬编码。这些应该使用sed
-ed 或打补丁。不要忘记让上游知道你的修改!
头文件和 Multilib
大多数 C/C++程序都需要标准的头文件,如types.h
。其中一些取决于特定于架构的事实,例如types.h
关于机器字的长度。为了确保我们可以同时编译 32 位和 64 位应用程序和库,我们对/usr/include/asm
进行了一些特殊处理。
这是/usr/include/asm/types.h
在 AMD64 框架上的样子:
/* Common header file autogenerated by create_ml_includes in multilib.eclass */
#ifdef __i386__
#include <asm-i386/types.h>
#endif /* __i386__ */
#ifdef __x86_64__
#include <asm-x86_64/types.h>
#endif /* __x86_64__ */
如您所见,这只是一个包装器,它根据给 gcc 的参数-D
决定所需的文件。如果尝试手工编译某些内容而忘记将-D__x86_64__
附加到CFLAGS
上,则可能会遇到麻烦。当然,使用搬运工具时这不是必需的。有关说明,请参见ABI 变量部分。
ABI 变量
每当 portage 在 amd64 上构建某些东西时,它都必须决定它应该是 32 位还是 64 位。如头文件和 Multilib中所述,CDEFINE
中分别需要__i386__
或__x86_64__
。另外,gcc 必须知道应该生成什么代码,因此-m32
或-m64
必须附加到 CFLAGS。这是通过profile.bashrc
完成的。如果要构建 32 位软件包,您所需要做的就是设置ABI=x86
。
详细信息显示在make.defaults
中:
MULTILIB_ABIS="x86 amd64"
DEFAULT_ABI="amd64"
CFLAGS_amd64="-m64"
LDFLAGS_amd64="-m elf_x86_64"
CHOST_amd64="x86_64-pc-linux-gnu"
CDEFINE_amd64="__x86_64__"
LIBDIR_amd64="lib64"
CFLAGS_x86="-m32 -L/emul/linux/x86/lib -L/emul/linux/x86/usr/lib"
LDFLAGS_x86="-m elf_i386 -L/emul/linux/x86/lib -L/emul/linux/x86/usr/lib"
CHOST_x86="i686-pc-linux-gnu"
CDEFINE_x86="__i386__"
LIBDIR_x86="lib32"
移植说明
机器字大小
在 AMD64 上,某些类型的大小与 x86 不同:
Type | x86 (ILP32) | amd64 (LP64) |
---|---|---|
char |
1 byte | 1 byte |
short |
2 bytes | 2 bytes |
int |
4 bytes | 4 bytes |
long |
4 bytes | 8 bytes |
long long |
8 bytes | 8 bytes |
pointer |
4 bytes | 8 bytes |
float |
4 bytes | 4 bytes |
double |
8 bytes | 8 bytes |
long double |
16 bytes | 16 bytes |
如果需要确切的空间量,请不要使用这些类型,而要使用types.h
提供的uXX
和sXX
,其中 XX 是所需的位数。不建议将两个架构上的类型切换为相同的类型,因为这不是一个干净的解决方案,并且可能会导致其他架构上的问题。
其它
许多上游开发人员都假定指针的长度为 4 个字节,这在程序将void*
强制转换为int
以及反之亦然时会引起问题。在 GCC 3.4 中,这会引起警告,编译不会中止。如果幸运的话,您的软件包可以运行,但是很可能会遇到段错误或奇怪的行为。 GCC 4.0 拒绝编译此类代码。