Solaris(64 位)开发者指南

第 5 章 开发环境

本章介绍 64 位应用程序开发环境以及生成环境,包括头文件和库问题、编译器选项、链接和调试工具。本章中的信息还将指导如何处理打包问题。

不过,在开始操作之前,必须首先确定所安装的操作系统是 32 位还是 64 位版本。如果不确定所安装的版本,则假设运行的是 64 位版本的操作系统。要进行确认,可以使用第 3 章,比较 32 位接口和 64 位接口中介绍的 isainfo(1) 命令。即使您使用的是 32 位操作环境,仍可以生成自己的 64 位应用程序,但前提是系统中包含系统头文件和 64 位库。

生成环境

生成环境包括系统头文件、编译系统和库,这些内容将在以下各节中进行介绍。

头文件

一组系统头文件可同时支持 32 位和 64 位编译环境。您无需为 64 位编译环境指定其他头文件路径。

应了解头文件 <sys/isa_defs.h> 中的各种定义,以便更好地了解为支持 64 位环境而对头文件进行的更改。该头文件包含一组已知的 #defines,并会为每个指令集体系结构设置这些指令。如果包含 <sys/types.h>,则会自动包含 <sys/isa_defs.h>

下表中的符号由编译环境定义:

符号 

说明 

__sparc

表示 SPARC 系列处理器体系结构,其中包括 SPARC V7、SPARC V8 和 SPARC V9 体系结构。符号 sparc__sparc 的前身。

__sparcv8

表示 SPARC Architecture Manual 版本 8 中所定义的 32 位 SPARC V8 体系结构。

__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

类型 intlong 和指针的长度均为 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 目标文件相对。

-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 位环境均可用。编译器和其他各种工具(例如 ldaras)是能够在 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 位动态链接程序的搜索路径。

$ORIGIN 关键字

分发和管理应用程序的一种常用方法就是将相关的应用程序和库放入一个简单的目录分层结构中。 通常,应用程序使用的库驻留在 lib 子目录中,而应用程序本身则驻留在基目录的 bin 子目录中。 该基目录随后可以使用 NFSTM(Sun 的分布式计算文件系统)导出并挂载到客户机上。 在某些环境中,自动挂载程序和名称服务可用来分发应用程序,并可确保应用程序分层结构的文件系统名称空间在所有客户机上都相同。 在此类环境中,可以在生成应用程序时,使用链接程序的 -R 标志来指定在运行时应当在其中搜索共享库的目录的绝对路径名。

但是在其他环境中,文件系统名称空间的控制并不是很好,并且开发者已转向使用调试工具(LD_LIBRARY_PATH 环境变量)来在包装脚本中指定库搜索路径。这是不必要的,因为可以在路径名(在链接程序的 -R 选项中指定)中使用 $ORIGIN 关键字。 $ORIGIN 关键字在运行时会扩展为可执行文件本身所在目录的名称。 实际上,这意味着可以使用相对于 $ORIGIN 的路径名来指定库目录的路径名。 这允许在完全未设置 LD_LIBRARY_PATH 的情况下重新定位应用程序的基目录。

此功能对于 32 位和 64 位应用程序均可用,并且需要考虑何时创建新的应用程序,以减少正确配置 LD_LIBRARY_PATH 时用户或脚本的依赖项。

有关更多详细信息,请参见《链接程序和库指南》

对 32 位和 64 位应用程序进行打包

以下几节将讨论对 32 位和 64 位应用程序进行打包时的注意事项。

库和程序的位置

SPARC。新库和新程序的位置遵循32 位和 64 位库中介绍的标准约定。32 位库仍然位于相同位置,而 64 位库则会放在常规缺省目录下与体系结构有关的特定目录中。特定于 32 位和 64 位的应用程序的位置对于用户应保持透明。

这意味着 32 位库应放在相同的库目录中,64 位库应放在相应的 lib 目录下的 sparcv9 子目录中。

如果程序要求使用特定于 32 位或 64 位环境的版本,则情况会稍有不同,此时,应将这些程序放在其通常所在目录的相应 sparcv7sparcv9 子目录中。

64 位库应放在相应的 lib 目录下的 amd64 子目录中。

如果程序要求使用特定于 32 位或 64 位环境的版本,则应将这些程序放在其通常所在目录的相应 i86amd64 子目录中。

请参见应用程序命名约定

打包原则

打包选项包括为 32 位和 64 位应用程序创建特定的软件包或者将 32 位和 64 位版本合并到一个软件包中。如果创建了单个软件包,则应当针对软件包的内容使用子目录命名约定,如本章中所述。

应用程序命名约定

库和程序的位置中所述,32 位和 64 位应用程序可以放在平台特定的相应子目录中,而不是为同一个应用程序的 32 位和 64 位版本指定特定的名称,如 foo32foo64。然后,可以使用包装(在下一节中进行介绍)来运行正确版本的应用程序。这样做的一个优点是用户无需了解具体版本(32 位或 64 位),因为系统会根据平台自动执行正确的版本。

Shell 脚本包装

在需要使用特定于 32 位和 64 位的应用程序版本的情况下,可以借助 shell 脚本包装使版本对于用户保持透明。如果 Solaris 操作环境中有许多工具需要使用 32 位和 64 位版本,则可以这样做。包装可以使用 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} "${@}"

此示例存在一个问题,即它要求 $0 参数是其可执行文件的全路径名。为此,创建了一个常规包装 isaexec(),以便解决特定于 32 位和 64 位应用程序的问题。下面将说明该包装。

/usr/lib/isaexec 二进制文件

isaexec(3C) 是一个 32 位可执行二进制文件,它执行上面刚介绍的 shell 脚本包装中概述的包装函数,但是具有准确完整的参数列表。该可执行文件的全路径名是 /usr/lib/isaexec,但是在执行时并不使用该名称,而是将该名称复制或链接(硬链接而不是软链接)到存在多个版本的程序的主名称,这些版本是通过 isalist(1) 进行选择的。

例如,在 SPARC 环境中,truss(1) 命令作为以下三个可执行文件存在:

/usr/bin/truss

/usr/bin/sparcv7/truss

/usr/bin/sparcv9/truss

sparcv7sparcv9 子目录中的可执行文件分别是实际的 32 位和 64 位 truss(1) 可执行文件。包装文件 /usr/bin/truss 是指向 /usr/lib/isaexec 的硬链接。

在 x86 环境中,truss(1) 命令作为以下三个可执行文件存在:

/usr/bin/truss

/usr/bin/i86/truss

/usr/bin/amd64/truss

isaexec(3C) 包装使用 getexecname(3C) 来确定它自己的经过完全解析的无符号链接路径名(与其 argv[0] 参数无关),sysinfo(SI_ISALIST, ...) 获取 isalist(1),并针对第一个可执行文件执行 exec(2),第一个可执行文件的名称可在为其所在的目录生成的子目录列表中找到。然后,该包装会按原样传递参数向量和环境向量。这样,传递给最后一个程序映像的 argv[0] 将显示为所指定的第一个参数,而不是转换成经修改以包含子目录名称的全路径名。


注 –

因为可能存在包装,所以在将可执行文件移到其他位置时,需要格外小心。您可能会移动包装而不是实际程序。


isaexec(3c) 接口

许多应用程序已经使用启动包装程序来设置环境变量、清除临时文件、启动守护进程等。 libc(3LIB) 中的 isaexec(3C) 接口允许直接从自定义包装程序调用以上基于 shell 的包装示例中所使用的算法。

调试 64 位应用程序

为了处理 64 位应用程序,所有 Solaris 调试工具均已进行了更新。这些工具包括 truss(1) 命令、/proc 工具 (proc(1)) 和 mdb

dbx 调试器作为 Sun Studio 工具套件的一部分提供,可用来调试 64 位应用程序。其余的工具是 Solaris 发行版附带的。

所有这些工具的选项都保持不变。mdb 中提供了许多增强功能,用来调试 64 位程序。 正如所预期的那样,使用 "*" 取消引用指针时,对于 64 位程序将取消引用 8 个字节,对于 32 位程序取消引用 4 个字节。 此外,还提供了以下修饰符:

Additional ?, /, = modifiers:

	

g		(8) Display 8 bytes in unsigned octal	

G		(8) Display 8 bytes in signed octal

e		(8) Display 8 bytes in signed decimal	

E		(8) Display 8 bytes in unsigned decimal

J		(8) Display 8 bytes in hexadecimal

K		(n) Print pointer or long in hexadecimal

		  Display 4 bytes for 32-bit programs

		  and 8 bytes for 64-bit programs.

y		(8) Print 8 bytes in date format

 

Additional ? and / modifiers:

 

M <value> <mask>  Apply <mask> and compare for 8-byte value;

		  move '.' to matching location.

Z		(8) write 8 bytes