第4 章• 转换应用程序25
提供使代码可在32 位和64 位环境之间移植的一般规则
实现单一源代码
以下几节介绍了一些可供应用程序开发者使用的资源,可帮助编写能够同时支持32 位编译
和64 位编译的单一源代码。
系统头文件 和 中包含有助于使应用程序对于32 位和64 位均安
全的常量、宏和派生类型。尽管详细讨论这些内容超出了本文档的范围,但是以下各节以
及附录A中仍讨论了其中的部分内容。
功能测试宏
包含 的应用程序源文件可以通过包括 使编程模型符号
_LP64 和_ILP32 的定义可用。
有关预处理程序符号(_LP64 和_ILP32)和宏(_LITTLE_ENDIAN 和_BIG_ENDIAN6)的信息,
请参见types(3HEAD)。
派生类型
使用系统派生类型有助于使代码对于32 位和64 位均安全,这是由于派生类型本身对于
ILP32 和LP64 数据模型均安全。通常,使用派生类型以便于更改是良好的编程做法。如果
数据模型在将来发生变化,或者在移植到其他平台时,只需更改系统派生类型即可,而无
需更改应用程序。
文件
头文件中包含大量应在适当时机使用的基本派生类型。特别是以下几种类型
颇受关注:
clock_t 类型clock_t 表示系统时间(以时钟周期为单位)。
dev_t 类型dev_t 用于设备号。
off_t 类型off_t 用于文件大小和偏移量。
ptrdiff_t 类型ptrdiff_t 是一种带符号整数类型,用于对两个指针执行减法运算
后所得的结果。
size_t 类型size_t 用于内存中对象的大小(以字节为单位)。
ssize_t 带符号的大小类型ssize_t 供返回字节计数或错误指示的函数使用。
time_t 类型time_t 用于时间(以秒为单位)。
实现单一源代码
26 Solaris(64 位)开发者指南• 2006 年11 月
所有这些类型在ILP32 编译环境中都保持32 位值,并会在LP64 编译环境中增加到64 位
值。
其中某些类型的用法将在本章第32 页中的“转换为LP64 的指导原则”中更详细地介绍。
文件
在Solaris 2.6 发行版中添加了头文件,程序员可利用它提供的常量、宏和派生
类型使其代码与显式指定大小的数据项兼容,而不管编译环境如何。该文件中包含用来处
理8 位、16 位、32 位和64 位对象的机制,它是ANSI C 议案的一部分,可以与
ISO/JTC1/SC22/WG14 C 委员会对当前ISO C 标准(即ISO/IEC 9899:1990 编程语言-C)所
进行修订的工作草案保持一致。
提供的基本功能包括:
一组定宽的整数类型
uintptr_t 和其他有用的类型
常量宏
限制
格式字符串宏
以下各节中将对这些功能进行更详细的讨论。
定宽的整数类型
提供的定宽整数类型同时包括带符号整数类型(如int8_t、int16_t、int32_t
和int64_t)以及无符号整数类型(如uint8_t、uint16_t、uint32_t 和uint64_t)。定义
为可具有指定位数的最短整数类型的派生类型包括int_least8_t、int_least64_t、
uint_least8_t 和uint_least64_t。
不应不加选择地使用这些定宽类型。例如,类型int 可以继续用于循环计数器和文件描述
符,类型long 可用于数组索引。另一方面,对于以下各项的显式二进制表示形式,则应使
用定宽类型:
盘上数据
线上数据
硬件寄存器
二进制接口规格(包含显式指定了大小的对象,或者涉及32 位和64 位程序之间的共享
或通信)
二进制数据结构(供32 位和64 位程序通过共享内存和文件等进行使用)
实现单一源代码
第4 章• 转换应用程序27
uintptr_t 和其他有用的类型
提供的其他有用类型包括大小足以包含一个指针的带符号整数类型和无符号
整数类型。这些类型以intptr_t 和uintptr_t 形式提供。此外,还会将intmax_t 和
uintmax_t 分别定义为可用的最长(以位为单位)带符号整数类型和无符号整数类型。
选用uintptr_t 类型作为指针的整数类型比使用基本类型(如unsigned long)要好。尽管
在ILP32 和LP64 数据模型中,类型unsigned long 与指针的长度相同,但如果使用
uintptr_t,则只需在使用其他数据模型时更改uintptr_t 的定义即可。这会使其可移植到
许多其他系统中,并且还可以在C 中更为清楚地表达意图。
需要执行地址运算时,intptr_t 和uintptr_t 类型对于强制转换指针非常有用。因此,应使
用这些类型,而不是使用long 或unsigned long 类型。
注– 使用uintptr_t 进行强制类型转换通常比使用intptr_t 安全,在进行比较时尤为安全。
常量宏
提供宏的目的在于指定给定常量的大小和符号。这些宏包括INT8_C(c)、INT64_C(c)、
UINT8_C(c) 和UINT64_C(c) 等。基本上,这些宏会在常量的末尾放置一个l、ul、ll 或ull
(如有必要)。例如,对于ILP32,INT64_C(1) 会在常量1 后面附加ll;对于LP64,则附
加l。
用来使常量成为最长类型的宏包括INTMAX_C(c) 和UINTMAX_C(c)。这些宏对于指定第32 页
中的“转换为LP64 的指导原则”中介绍的常量类型会非常有用。
定义的限制
由 定义的限制是用于为各种整数类型指定最小值和最大值的常量,其中包括
每个定宽类型的最小值(INT8_MIN 和INT64_MIN 等)和最大值(如INT8_MAX 和INT64_MAX
等)及其对应的无符号的最小值和最大值。
该文件还提供了每个最短长度类型的最小值和最大值,其中包括INT_LEAST8_MIN、
INT_LEAST64_MIN、INT_LEAST8_MAX 和INT_LEAST64_MAX 等及其对应的无符号的最小值和最大
值。
最后,该文件定义了支持的最长整数类型的最小值和最大值,其中包括INTMAX_MIN 和
INTMAX_MAX 及其对应的无符号的最小值和最大值。
格式字符串宏
文件还提供了用于指定printf 和scanf 格式说明符的宏。实际上,这些宏会
根据内置于宏名称中的参数的位数,在格式说明符前面放置一个l 或ll,将参数指定为
long 或long long 类型。
实现单一源代码
28 Solaris(64 位)开发者指南• 2006 年11 月
printf(3C) 的宏以十进制、八进制、无符号和十六进制格式列显8 位、16 位、32 位和64 位
整数以及最短和最长整数类型。例如,以十六进制表示法列显64 位整数:
int64_t i;
printf("i =%" PRIx64 "\n", i);
同样,scanf(3C) 的宏也以十进制、八进制、无符号和十六进制格式读取8 位、16 位、32 位
和64 位整数以及最长整数类型。例如,读取无符号的64 位十进制整数:
uint64_t u;
scanf("%" SCNu64 "\n", &u);
请勿毫无限制地使用这些宏,最好将其与定宽类型一起使用。有关更多详细信息,请参阅
第27 页中的“定宽的整数类型”一节。
工具支持
Sun Studio 10 编译器中提供的lint 程序可以检测潜在的64 位问题,并有助于使代码对于64
位安全。此外,该C 编译器的-v 选项也会非常有用。该选项可指示编译器执行更严格的附
加语义检查,还会针对指定的文件启用某些与lint 相似的检查。
有关C 编译器和lint 的功能的更多信息,请参见《Sun Studio 10: C User’s Guide》。
用于32 位和64 位环境的lint
lint 既可用于32 位代码又可用于64 位代码。对于要在32 位和64 位环境中运行的代码,请
使用-errchk=longptr64 选项。-errchk=longptr64 选项用于检查是否可将代码移植到长整
数和指针的长度为64 位、无格式整数的长度为32 位的环境中。
对于要在64 位SPARC 环境中运行的lint 代码,应当使用-Xarch=v9 选项。将
-errchk=longptr64 选项和-Xarch=v9 选项一起使用,可以针对要在64 位SPARC 上运行的代
码生成有关潜在64 位程序问题的警告。
从Solaris 10 发行版开始,对于要在64 位AMD环境中运行的lint 代码,应当使用
-Xarch=amd64 选项。
注– lint 的-D__sparcv9 选项已不再需要,不应当再使用。
有关lint 选项的说明,请参见《Sun Studio 10: C User’s Guide》。
工具支持
第4 章• 转换应用程序29
生成警告时,lint(1) 会列显违例代码的行号、描述问题的警告消息以及是否涉及指针的注
释。它还会指明所涉及类型的长度。如果确定涉及指针并且知道数据类型的长度,则会便
于查找特定的64 位问题,并避免32 位和更短类型之间的已有问题。
注– 尽管lint 会给出有关潜在的64 位问题的警告,但也无法检测所有的问题。请务必牢
记,在lint 生成的警告中,并非所有警告都是真正的64 位问题。在许多情况下,符合应用
程序意图并且正确无误的代码也会生成警告。
以下的程序和lint(1) 输出样例说明了非纯64 位代码中可能出现的大多数lint 警告。
1 #include
2 #include
3 #include
4
5 static char chararray[] = "abcdefghijklmnopqrstuvwxyz";
6
7 static char *myfunc(int i)
8 {
9 return(& chararray[i]);
10 }
11
12 void main(void)
13 {
14 int intx;
15 long longx;
16 char *ptrx;
17
18 (void) scanf("%d", &longx);
工具支持
30 Solaris(64 位)开发者指南• 2006 年11 月
19 intx = longx;
20 ptrx = myfunc(longx);
21 (void) printf("%d\n", longx);
22 intx = ptrx;
23 ptrx = intx;
24 intx = (int)longx;
25 ptrx = (char *)intx;
26 intx = 2147483648L;
27 intx = (int) 2147483648L;
28 ptrx = myfunc(2147483648L);
29 }
(19) warning: assignment of 64-bit integer to 32-bit integer
(20) warning: passing 64-bit integer arg, expecting 32-bit integer: myfunc(arg 1)
(22) warning: improper pointer/integer combination: op "="
(22) warning: conversion of pointer loses bits
(23) warning: improper pointer/integer combination: op "="
(23) warning: cast to pointer from 32-bit integer
(24) warning: cast from 64-bit integer to 32-bit integer
(25) warning: cast to pointer from 32-bit integer
(26) warning: 64-bit constant truncated to 32 bits by assignment
(27) warning: cast from 64-bit integer constant expression to 32-bit integer
(28) warning: passing 64-bit integer constant arg, expecting 32-bit integer: myfunc(arg 1)
function argument ( number ) type inconsistent with format
工具支持
第4 章• 转换应用程序31
scanf (arg 2) long * :: (format) int * t.c(18)
printf (arg 2) long :: (format) int t.c(21)
(仅当常量表达式不适合它所强制转换到的类型时,才会发出从以上代码样例的第27 行中
生成的lint 警告。)
通过在上一行中加一个/*LINTED*/ 注释,可以禁止对给定的源代码行发出警告。如果确实
要让代码以特定方式运行(例如强制类型转换和赋值)时,这种做法会很有用。使用
/*LINTED*/ 注释时请务必谨慎,因为它可能会掩盖真实问题。有关更多信息,请参阅《Sun
Studio 10: C User’s Guide》或lint(1) 手册页。
转换为LP64 的指导原则
使用lint(1) 时,请记住,并非所有问题都将生成lint(1) 警告,并且并非所有lint(1) 警
告都表示要求进行更改。请针对不同意图检查各种可能性。以下示例说明了转换代码时可
能遇到的一些常见问题。在适当情况下,会显示对应的lint(1) 警告。
请勿假设int 和指针的长度相同
由于类型为int 的数据和指针在ILP32 环境中长度相同,因此许多代码会依赖于这一假设。
通常会将指针强制转换为int 或unsigned int 类型以进行地址运算。但是,由于类型为
long 的数据和指针在ILP32和LP64 数据类型模型中长度均相同,因此可以将指针强制转换
为long 类型。请使用类型uintptr_t 而不是显式使用unsigned long,因为前者可更贴切地
表达意图并使代码具有更强的可移植性,从而使其不会受到将来变化的影响。例如,
char *p;
p = (char *) ((int)p & PAGEOFFSET);
将生成以下警告:
warning: conversion of pointer loses bits
使用以下代码将生成不带警告的结果:
char *p;
p = (char *) ((uintptr_t)p & PAGEOFFSET);
转换为LP64 的指导原则
32 Solaris(64 位)开发者指南• 2006 年11 月
请勿假设int 和long 的长度相同
由于类型为int 和long 的数据在ILP32 中从未真正加以区分,因此许多现有的代码会隐式
或显式地假设它们可互换使用,并不加区分地进行使用。必须修改做出此假设的任何代
码,使其可同时用于ILP32 和LP64。类型int 和long 在ILP32 数据类型模型中均为32 位,
而类型long 在LP64 数据模型中则为64 位。例如,
int waiting;
long w_io;
long w_swap;
...
waiting = w_io + w_swap;
将生成以下警告:
warning: assignment of 64-bit integer to 32-bit integer
符号扩展
转换到64 位时,经常会遇到非故意符号扩展问题。该问题很难在实际发生前检测到,因为
lint(1) 不会针对它发出警告。此外,类型转换和提升规则也有些模糊。要解决非故意符号
扩展的问题,必须使用显式强制类型转换来实现预期结果。
要了解出现符号扩展的原因,了解ANSI C 的转换规则会有所帮助。以下是可能会导致32 位
和64 位整数值之间大多数符号扩展问题的转换规则:
1. 整型提升
无论有无符号,均可以在任何调用类型为int 的表达式中使用char、short、枚举类型或
位字段。如果int 可以支持初始类型的所有可能值,则值会转换为int 类型。否则,值
会转换为unsigned int 类型。
2. 带符号整数和无符号整数之间的转换
将带负号的整数提升为同一类型或更长类型的无符号整数时,该整数首先提升为更长类
型的带符号相同值,然后再转换为无符号值。
有关转换规则的更详细讨论,请参阅ANSI C 标准。该标准中还包括适用于普通运算转换和
整数常量的规则。
以下示例编译为64 位程序时,即使addr 和a.base 均是unsigned 类型,addr 变量仍可成为
带符号扩展变量。
转换为LP64 的指导原则
第4 章• 转换应用程序33
示例4–1test.c
struct foo {
unsigned int base:19, rehash:13;
};
main(int argc, char *argv[])
{
struct foo a;
unsigned long addr;
a.base = 0x40000;
addr = a.base << 13; /* Sign extension here! */
printf("addr 0x%lx\n", addr);
addr = (unsigned int)(a.base << 13); /* No sign extension here! */
printf("addr 0x%lx\n", addr);
}
进行此符号扩展的原因是按以下方式应用了转换规则:
1. a.base 由于整型提升规则而从类型unsigned int 转换为int。因此,表达式a.base <<
13 的类型为int,但是未进行符号扩展。
2. 表达式a.base << 13 的类型为int,但是在赋值给addr 之前,由于带符号和无符号整型
提升规则,会转换为类型long,然后再转换为类型unsigned long。从类型int 转换为
long 时,会进行符号扩展。
% cc -o test64 -xarch=v9 test.c
% ./test64
addr 0xffffffff80000000
转换为LP64 的指导原则
34 Solaris(64 位)开发者指南• 2006 年11 月
addr 0x80000000
%
如果将同一示例编译为32 位程序,则不显示任何符号扩展:
% cc -o test32 test.c
% ./test32
addr 0x80000000
addr 0x80000000
%
使用指针运算而不是地址运算
通常,由于指针运算是独立于数据模型的,而地址运算则可能不是,因此使用指针运算比
地址运算更好。此外,使用指针运算通常还可以简化代码。例如,
int *end;
int *p;
p = malloc(4 * NUM_ELEMENTS);
end = (int *)((unsigned int)p + 4 * NUM_ELEMENTS);
将生成以下警告:
warning: conversion of pointer loses bits
以下代码将生成不带警告的结果:
int *end;
int *p;
p = malloc(sizeof (*p) * NUM_ELEMENTS);
end = p + NUM_ELEMENTS;
转换为LP64 的指导原则
第4 章• 转换应用程序35
对结构重新压缩
由于long 和指针字段在LP64 中会增加到64 位,因此编译器可能会向结构中添加额外的填
充内容,以满足对齐要求。对于SPARCV9ABI 和amd64 ABI,所有类型的结构均与结构中
最长成员的长度对齐。对结构重新压缩时,一个简单的规则就是,将long 和指针字段移到
结构开头,然后重新排列其余的字段,通常(但不总是)按长度的降序排列,具体取决于
这些字段可以压缩的程度。例如,
struct bar {
int i;
long j;
int k;
char *p;
}; /* sizeof (struct bar) = 32 */
要获取更好的结果,请使用:
struct bar {
char *p;
long j;
int i;
int k;
}; /* sizeof (struct bar) = 24 */
注– 基本类型的对齐方式在i386 和amd64ABI 之间会发生变化。请参见第58 页中的“对齐
问题”。
检查联合类型
请确保对联合类型进行检查,因为其字段的长度在ILP32 和LP64 数据类型模型之间可能会
发生变化。例如,
typedef union {
转换为LP64 的指导原则
36 Solaris(64 位)开发者指南• 2006 年11 月
double _d;
long _l[2];
} llx_t;
应当为:
typedef union {
double _d;
int _l[2];
} llx_t;
指定常量类型
在某些常量表达式中,由于精度不够而会导致数据丢失。这些类型的问题很难发现。请在
常量表达式中显式指定数据类型。通过向每个整型常量的末尾增加{u,U,l,L} 的某些组合,
可以指定其类型。另外,也可以使用强制类型转换来指定常量表达式的类型。例如,
int i = 32;
long j = 1 << i; /* j will get 0 because RHS is integer expression */
应当为:
int i = 32;
long j = 1L << i;
注意隐式声明
在某些编译模式下,编译器可能会假设针对在模块中使用却未在外部定义或声明的任何函
数或变量,使用int 类型。编译器的隐式int 类型声明会将以此方式使用的任何类型为long
的数据和指针截断。函数或变量的相应extern 类型声明应置于头文件而非C 模块中。然
后,使用此函数或变量的任何C 模块中应包含此头文件。如果是系统头文件定义的函数或
变量,则仍然需要在代码中包含正确的头文件。
例如,由于未声明getlogin(),因此以下代码:
int
转换为LP64 的指导原则
第4 章• 转换应用程序37
main(int argc, char *argv[])
{
char *name = getlogin()
printf("login = %s\n", name);
return (0);
}
将生成以下警告:
warning: improper pointer/integer combination: op "="
warning: cast to pointer from 32-bit integer
implicitly declared to return int
getlogin printf
要获取更好的结果,请使用:
#include
#include
int
main(int argc, char *argv[])
{
char *name = getlogin();
(void) printf("login = %s\n", name);
return (0);
}
转换为LP64 的指导原则
38 Solaris(64 位)开发者指南• 2006 年11 月
sizeof 是unsigned long
在LP64 环境中,sizeof 的有效类型为size_t,该类型会实现为unsigned long。有时,
sizeof 会传递给需要使用类型为int 的参数的函数,或者赋值给int 类型的数据或强制转换
为int 类型。在某些情况下,这种截断可能会导致数据丢失。例如,
long a[50];
unsigned char size = sizeof (a);
将生成以下警告:
warning: 64-bit constant truncated to 8 bits by assignment
warning: initializer does not fit or is out of range: 0x190
使用强制类型转换说明意图
关系表达式可能会因转换规则而变得错综复杂。您应当通过在必要的地方添加强制类型转
换来非常明确地指定表达式的求值方式。
检查格式字符串转换操作
对于类型为long 或指针的参数,可能需要更改printf(3C)、sprintf(3C)、scanf(3C) 和
sscanf(3C) 的格式字符串。对于要同时在32 位和64 位环境中运行的指针参数,格式字符串
中给定的转换操作应为%p。例如,
char *buf;
struct dev_info *devi;
...
(void) sprintf(buf, "di%x", (void *)devi);
将生成以下警告:
warning: function argument (number) type inconsistent with format
sprintf (arg 3) void *: (format) int
使用以下代码将生成不带警告的结果:
转换为LP64 的指导原则
第4 章• 转换应用程序39
char *buf;
struct dev_info *devi;
...
(void) sprintf(buf, "di%p", (void *)devi);
另外,请检查以确保buf 指向的存储器大小足以包含16 个数字。对于类型为long 的参数,
long 类型的长度规范l 应置于格式字符串中转换操作字符的前面。例如,
size_t nbytes;
ulong_t align, addr, raddr, alloc;
printf("kalloca:%d%%%d from heap got %x.%x returns %x\n",
nbytes, align, (int)raddr, (int)(raddr + alloc), (int)addr);
将生成以下警告:
warning: cast of 64-bit integer to 32-bit integer
warning: cast of 64-bit integer to 32-bit integer
warning: cast of 64-bit integer to 32-bit integer
以下代码将生成不带警告的结果:
size_t nbytes;
ulong_t align, addr, raddr, alloc;
printf("kalloca:%lu%%%lu from heap got %lx.%lx returns %lx\n",
nbytes, align, raddr, raddr + alloc, addr);
其他注意事项
其余指导原则将重点说明将应用程序转换为完全64 位程序时遇到的常见问题。
其他注意事项
40 Solaris(64 位)开发者指南• 2006 年11 月
长度增加的派生类型
许多派生类型已进行了更改,以便在64 位应用程序环境中表示64 位值。此更改不会影响32
位应用程序;但是,使用或导出这些类型所描述的数据的任何64 位应用程序均需要重新求
值以确保正确。关于这一点的一个示例是直接处理utmpx(4) 文件的应用程序。要在64 位应
用程序环境中正确操作,请勿尝试直接访问这些文件,而应当使用getutxent(3C) 及相关的
函数系列。
附录A中包括了更改的派生类型的列表。
检查更改的副作用
需要注意的一个问题是,一个区域中的类型更改可能会导致另一个区域中进行意外的64 位
转换。例如,如果某个函数以前返回类型int 而现在返回ssize_t,则需要检查所有调用
方。
检查直接使用long 类型是否仍有意义
由于类型long 在ILP32 模型中为32 位,在LP64 模型中为64 位,因此可能会出现以前定义
为long 类型的数据既不恰当又不必要的情况。在这种情况下,请尽可能使用可移植性更强
的派生类型。
与此相关的是,由于上述原因,许多派生类型在LP64 数据类型模型中可能已更改。例如,
pid_t 在32 位环境中仍为long 类型,但是在64 位环境中,pid_t 则为int 类型。有关针对
LP64 编译环境修改的派生类型的列表,请参见附录A。
对显式32 位与64 位原型使用#ifdef
在某些情况下,一个接口存在特定的32 位和64 位版本是不可避免的。可以通过在头文件中
使用_LP64 或_ILP32 功能测试宏来区分这些版本。同样,要在32 位和64 位环境中运行的代
码也需要利用相应的#ifdefs,具体取决于编译模式。
算法更改
在确认代码对64 位环境是安全的之后,请再次检查代码,以验证算法和数据结构是否仍有
意义。由于数据类型变大,因此数据结构可能会使用更多空间。代码的性能也可能变化。
考虑到以上几点,您可能需要相应地修改代码。
其他注意事项
第4 章• 转换应用程序41
入门清单
假设需要将代码转换为64 位,以下清单可能会有所帮助:
阅读整个文档,重点阅读第32 页中的“转换为LP64 的指导原则”。
检查所有数据结构和接口,验证它们在64 位环境中仍然有效。
在代码中包含,以获取_ILP32 或_LP64 定义以及多种基本派生类型。
将函数原型以及具有非局部作用域的外部声明移到头文件中,并将这些头文件包含在代
码中。
使用-errchk=longptr64 选项运行lint(1),并单独检查每个警告。请注意,并非所有警
告都要求对代码进行更改。根据所进行的更改,可能还需要在32 位和64 位模式下再次
运行lint(1)。
除非提供的应用程序仅为64 位,否则请将代码编译为32 位和64 位两种形式。
测试应用程序,方法是在32 位操作系统上执行32 位版本,在64 位操作系统上执行64 位
版本。也可以在64 位操作系统上测试32 位版本,但这不是必需的。
程序样例
以下程序样例foo.c 直接对比说明了LP64 数据模型与ILP32 数据模型的不同结果。同一个
程序既可以编译为32 位程序,也可以编译为64 位程序。
#include
int
main(int argc, char *argv[])
{
(void) printf("char is \t\t%lu bytes\n", sizeof (char));
(void) printf("short is \t%lu bytes\n", sizeof (short));
(void) printf("int is \t\t%lu bytes\n", sizeof (int));
(void) printf("long is \t\t%lu bytes\n", sizeof (long));
(void) printf("long long is \t\t%lu bytes\n", sizeof (long long));
(void) printf("pointer is \t%lu bytes\n", sizeof (void *));
return (0);
入门清单
42 Solaris(64 位)开发者指南• 2006 年11 月
}
32 位编译的结果是:
% cc -O -o foo32 foo.c
% foo32
char is 1 bytes
short is 2 bytes
int is 4 bytes
long is 4 bytes
long long is 8 bytes
pointer is 4 bytes
64 位编译的结果是:
% cc -xarch=generic64 -O -o foo64 foo.c
% foo64
char is 1 bytes
short is 2 bytes
int is 4 bytes
long is 8 bytes
long long is 8 bytes
pointer is 8 bytes
注– 缺省编译环境旨在最大化可移植性,即创建32 位应用程序。
程序样例
第4 章• 转换应用程序43
44
开发环境
本章介绍64 位应用程序开发环境以及生成环境,包括头文件和库问题、编译器选项、链接
和调试工具。本章中的信息还将指导如何处理打包问题。
不过,在开始操作之前,必须首先确定所安装的操作系统是32 位还是64 位版本。如果不确
定所安装的版本,则假设运行的是64 位版本的操作系统。要进行确认,可以使用第3 章中
介绍的isainfo(1) 命令。即使您使用的是32 位操作环境,仍可以生成自己的64 位应用程
序,但前提是系统中包含系统头文件和64 位库。
生成环境
生成环境包括系统头文件、编译系统和库,这些内容将在以下各节中进行介绍。
头文件
一组系统头文件可同时支持32 位和64 位编译环境。您无需为64 位编译环境指定其他头文
件路径。
应了解头文件 中的各种定义,以便更好地了解为支持64 位环境而对头文
件进行的更改。该头文件包含一组已知的#defines,并会为每个指令集体系结构设置这些
指令。如果包含,则会自动包含。
下表中的符号由编译环境定义:
符号说明
__sparc 表示SPARC 系列处理器体系结构,其中包括SPARC V7、SPARC V8 和SPARC V9 体
系结构。符号sparc 是__sparc 的前身。
__sparcv8 表示SPARCArchitecture Manual 版本8 中所定义的32 位SPARC V8 体系结构。
5第5 章
45
符号说明
__sparcv9 表示SPARC Architecture Manual 版本9 中所定义的64 位SPARC V9 体系结构。
__x86 表示x86 系列处理器体系结构,其中包括386、486、Pentium、IA-32、AMD64 和
EM64T 处理器。
__i386 表示32 位i386 体系结构。
__amd64 表示64 位amd64 体系结构。
注– __i386 和__amd64 互斥。符号__sparcv8 和__sparcv9 互斥并且仅当定义符号__sparc 时
才相关。
以下符号是从上面所定义的符号的一些组合派生而来的:
符号说明
_ILP32 类型int、long 和指针的长度均为32 位的数据模型。
_LP64 类型long 和指针的长度均为64 位的数据模型。
注– 符号_ILP32 和_LP64 互斥。
如果无法编写完全可移植的代码,并且需要特定的32 位与64 位代码,请使用_ILP32 或
_LP64 在代码中设置条件。这会使编译环境独立于计算机,并且最大程度地增强应用程序对
所有64 位平台的可移植性。
编译器环境
为了同时支持32 位应用程序和64 位应用程序的创建,Sun Studio C、C++ 和Fortran 编译环
境已进行了增强。Sun Studio 中10.0 发行版的C 编译器可提供64 位编译支持。
本机编译和交叉编译模式均受支持。缺省编译环境仍然可以生成32 位应用程序。尽管这两
种模式均受支持,但是它们仍特定于体系结构。使用Sun 编译器无法在x86 计算机上创建
SPARC 对象,也无法在SPARC 计算机上创建x86 对象。如果缺少体系结构规范或编译模
式,则缺省情况下会定义相应的__sparcv8 或__i386 符号,在此过程中还会定义_ILP32。
这会最大程度地提高与现有应用程序和硬件库的互操作性。
从Sun Studio 8 发行版开始,可使用cc(1) -xarch=generic64 标志来启用64 位编译环境。
这会在ELF64 对象中生成LP64 代码。ELF64 是支持64 位处理器和体系结构的64 位目标文
件格式,它与在缺省32 位模式下进行编译时生成的ELF32 目标文件相对。
生成环境
46 Solaris(64 位)开发者指南• 2006 年11 月
-xarch=generic64 标志用来在32 位或64 位系统中生成64 位代码。使用32 位编译器可以在
32 位系统中生成64 位对象;但是,不能在32 位系统中运行64 位对象。无需为64 位库指定
库路径。如果使用-l 或-L 选项来指定其他库或库路径,并且该路径仅指向32 位库,则链
接程序会检测到这一情况,同时将失败并显示错误。
有关编译器选项的说明,请参见《Sun Studio 10 C 用户指南》。
32 位和64 位库
Solaris 操作环境为32 位和64 位编译环境均提供了共享库。
32 位应用程序必须与32 位库链接,64 位应用程序必须与64 位库链接。不能使用64 位库来
创建或执行32 位应用程序。32 位库仍位于/usr/lib 和/usr/ccs/lib 中。64 位库位于相应
的lib 目录的子目录中。由于32 位库的位置没有变化,因此在早期发行版中生成的32 位应
用程序保持二进制兼容。可移植makefile 应当使用64 位符号链接来引用任何库目录。
为了生成64 位应用程序,需要使用64 位库。可以在本机或交叉编译模式下生成,因为64
位库对于32 位和64 位环境均可用。编译器和其他各种工具(例如ld、ar 和as)是能够在
32 位或64 位系统中生成64 位程序的32 位程序。当然,在运行32 位操作系统的系统中生成
的64 位程序不能在该32 位环境中执行。
链接目标文件
链接程序仍然是32 位应用程序,但是该应用程序对于大多数用户应保持透明,因为它通常
是由编译器驱动程序(例如cc(1))间接调用的。如果向链接程序提供ELF32 目标文件的集
合作为输入,则它会创建ELF32 输出文件;同样,如果向链接程序提供ELF64 目标文件的
集合作为输入,则它会创建ELF64 输出文件。链接程序不允许尝试混合使用ELF32 和
ELF64 输入文件。
LD_LIBRARY_PATH 环境变量
SPARC。32 位应用程序和64 位应用程序的动态链接程序分别是/usr/lib/ld.so.1 和
/usr/lib/sparcv9/ld.so.1。
x86。对于AMD64 体系结构,32 位应用程序和64 位应用程序的动态链接程序分别是
/usr/lib/ld.so.1 和/usr/lib/amd64/ld.so.1。
在运行时,这两个动态链接程序会搜索LD_LIBRARY_PATH 环境变量所指定的用冒号分隔的相
同目录列表。但是,32 位动态链接程序仅绑定到32 位库,而64 位动态链接程序则仅绑定
到64 位库。因此,如有必要,可通过LD_LIBRARY_PATH 来指定同时包含32 位和64 位库的目
录。
使用LD_LIBRARY_PATH_64 环境变量可以完全覆盖64 位动态链接程序的搜索路径。
链接目标文件
阅读(685) | 评论(0) | 转发(0) |