Solaris下开发64位程序的注意事项

1. ABI(Application Binary Interface):在Intel或AMD体系结构下,32位Solaris程序基于i386 ABI,64位程序基于AMD64 ABI;在SPARC体系结构下,32位程序是基于SPARC V8 ABI,64位的程序基于SPARC V9 ABI。可以用以下命令查看当前Solaris平台支持特性:
uname –a   查看操作系统版本
isainfo –v    查看操作系统支持的ABI
isainfo –b    查看操作系统内核的位数
isainfo –x    查看操作系统支持的指令集
至于在源代码中如何区分平台,可以通过包含,然后检查以下宏定义:

宏定义 说明
__x86 所有x86的处理器架构都会定义此宏,包括386, 486, Pentium, IA-32, AMD64, EM64T。
__i386 输出程序采用32-bit的i386 ABI时会定义此宏,它不能与__amd64同时定义。
__amd64 输出程序采用64-bit的AMD64 ABI时会定义此宏,它不能与__i386同时定义。
__sparc 所有SPARC V7, SPARC V8 与 SPARC V9平台都会定义此宏。
__sparcv8 输出程序采用32-bit的SPARC V8 ABI时会定义此宏,它不能与__sparcv9同时定义。
__sparcv9 输出程序采用64-bit的SPARC V9 ABI时会定义此宏,它不能与__sparcv8同时定义。

另外,Indel与AMD平台会定义_LITTLE_ENDIAN,SPARC平台会定义_BIG_ENDIAN

2. 数据模型:与其他UNIX及Linux一样,32位Solaris使用ILP32,64位Solaris使用LP64模型(基本数据类型中只有指针类型与long类型变为了64位),使用gcc在编译时会分别定义 _ILP32 _LP64(不需要包含任何头文件)。关于几种数据模型的差别可以查看相关资料,在此不做过多讨论,唯一值得注意的是:由于Win64采用LLP64模型(指针为64位,但long类型仍然为32位),因此如果要编写跨平台的应用程序,必须小心处理long型变量在两种数据模型中的长度差异。具体类型定义可以用下面的命令进行查询:
man 3 types
建议包含,该头文件里定义了int8_t,uint8_t,…,int64_t及uint64_t等跨平台的类型,且定义了INT8_MIN,UINT8_MIN,…,UINT64_MAX等极值。另外,在LP64模型中,因为指针都是64位,所以在一些需要将指针转化为整数值进行计算的算法中,如果直接将指针强转为int或long会面临兼容性问题,这种情况下建议将指针转换为 intptr_t 或 uintptr_t,它们是在任何平台下都保证能存下指针的数值类型。

3.字符常量:在使用64位编程时尤其要注意使用l,ul,ll,ull或INT8_C(c), ...,INT64_C(c), UINT8_C(c,...,UINT64_C(c)来声明const literal,否则将一个无符号数赋给64位变量时极易出错,例如:
unsigned long  i = –1U;    //在LP64中,i被赋值为0xFFFFFFFF,而非0xFFFFFFFFFFFFFFFF
但 unsigned long i = –1; 在LP64中,i被赋值为0xFFFFFFFFFFFFFFFF,-1为int32,先提升至int64(这时会扩展符号位),再转为uint64,因此将全为F。

4.默认编译参数:为了与现有的大量32位程序保持兼容,如果不附加额外的参数,gcc编译器默认以32位的ABI编译代码,生成的ELF32格式的二进制文件(即使在64位的Solaris),例如:
gcc  test.c
如果在Intel 或 AMD平台下,上面的命令将以i386 ABI生成二进制文件;如果在SPARC平台下,将以SPARC V8 ABI生成二进制文件。注意:虽然gcc可以在64位环境中交叉编译32位的程序,但不能在Intel或AMD平台中生成SPARC ABI的对象,反之亦然。
如果要生成ELF64的对象,需要添加-xarch=generic64-m64的编译参数,如:
gcc –m64 test.c
注意:用32位的编译器也能生成64位的程序,但是不能在32位系统中运行;另外,并非所有32位程序都能在64位平台上编译通过,或者即使编译过也不一定与原来32位平台下的特性一致,例如使用了/proc接口的一些库与程序。

5.链接搜索目录:链接中间文件与静态库时,所有文件的ELF(Executable and Linking Format)必须一致,如果最终生成的是32位程序,则要求所有中间文件与静态库都是ELF32格式;如果最终生成的是64位程序,则要求所有中间文件与静态库都是ELF64格式;如果出现混合,编译器会报错。如果程序还链接了动态库,则要求程序执行时能搜索到对应格式的动态库,否则执行时会出错。编译器与执行时的搜索目录是通过LD_LIBRARY_PATHLD_LIBRARY_PATH_64两个环境变量来控制。其中LD_LIBRARY_PATH_64只对64位程序有效,如果没有设定此环境变量,再使用LD_LIBRARY_PATH,这时很可能与32位的库混淆,具体32位库与64位库的位置可以参看系统文档与配置。

6. 64位下的调试:一般64位机器上既装了32位的gdb(gdb32),也安装了64位的gdb(gdb64),64位的程序只能用gdb64进行调试,因此在调试时注意gdb的版本。

7. 执行文件分发:假如我们现在又一个叫myapp的工程,编译了i386,amd64,sparc_v7,sparc_v9等多个版本,为了方便一次性分发,使其在不同平台下执行不同的程序,我们最好将各个版本的执行文件分别放置在几个子目录中,如:
./amd64/myapp
./i386/myapp
./sparcv9/myapp
./sparcv7/myapp
然后在基目录编写下面的脚本,在下面的脚本执行时会使用isalist获取平台支持的特性,遍历各个子目录,执行第一个被找到的程序:

#! /bin/sh
CMD=‘basename $0‘
DIR=‘dirname $0‘
EXEC=
for isa in ‘/usr/bin/isalist‘; do
    if [-x ${DIR}/${isa}/${CMD}]; then
         EXEC=${DIR}/${isa}/${CMD}
         break
    fi
done
if [-z "${EXEC}"]; then
echo 1>&2 "$0: no executable for this architecture"
exit 1
fi
exec ${EXEC} "${@}"

另外,这个脚本的功能也可以在C代码中通过isaexec()函数来实现。

 

参考文档:http://docs.sun.com/app/docs/doc/816-5138/preface-1?l=zh-TW&a=view

你可能感兴趣的:(gcc,Solaris,Path,library,平台,编译器)