Sun Studio 12:Fortran 编程指南

第 4 章 库

本章介绍如何使用和创建子程序库。对静态动态库均进行了讨论。

4.1 认识库

软件通常是先前已编译并组织成单个二进制库文件的子程序集。集中的每个成员称为库元素模块。链接程序搜索库文件,在生成可执行二进制程序时加载用户程序所引用的目标模块。有关详细信息,请参见 ld(1) 和 Solaris《链接程序和库指南》。

软件库有两种基本类型:

既有静态 (.a) 版本又有动态 (.so) 版本的典型系统库有:

使用库有两个优点:

库文件为程序共享常用子例程提供了一条简单途径。只需在链接程序时给出库名便可,那些解析程序中引用的库模块将被链接并合并到可执行文件中。

4.2 指定链接程序调试选项

通过 LD_OPTIONS 环境变量向链接程序传递其他选项,可以获得库用法和库加载方面的摘要信息。在生成目标二进制文件时,编译器会用这些选项(以及它要求的其他选项)调用链接程序。

始终建议使用编译器调用链接程序,而不是直接调用链接程序,因为许多编译器选项要求特定的链接程序选项或库引用,缺少这些,链接时会产生无法预料的结果。 示例:使用 LD_OPTIONS 创建加载映射


demo% setenv LD_OPTIONS ’–m -Dfiles’
demo% f95 -o myprog myprog.f

某些链接程序选项具有等价的编译器命令行选项,它们可以直接在 f95 命令中出现。它们包括 -Bx-dx-G-hname-Rpath-ztext。有关详细信息,请参见 f95(1) 手册页或《Fortran 用户指南》。

在 Solaris《链接程序和库指南》中,可以找到链接程序选项和环境变量的更多详细示例和解释。

4.2.1 生成加载映射

链接程序 -m 选项会生成显示链接信息的加载映射。可执行二进制程序生成期间链接的例程会与其源自的库一起被列出。

示例:使用 –m 生成加载映射:


demo% setenv LD_OPTIONS ’-m’
demo% f95 any.f
any.f:
 MAIN:
            LINK EDITOR MEMORY MAP

output    input    virtual
section   section  address         size

.interp            100d4          11
            .interp 100d4         11 (null)
.hash              100e8          2e8
            .hash    100e8        2e8 (null)
.dynsym            103d0          650
            .dynsym 103d0         650 (null)
.dynstr            10a20          366
            .dynstr 10a20         366 (null)
.text              10c90          1e70
.text              10c90    00 /opt/SUNWspro/lib/crti.o
.text              10c90    f4 /opt/SUNWspro/lib/crt1.o
.text              10d84    00 /opt/SUNWspro/lib/values-xi.o
.text              10d88    d20 sparse.o
...

4.2.2 列出其他信息

其他链接程序调试功能可通过链接程序的 -Dkeyword 选项获得。使用 -Dhelp 选项可以显示完整的列表。

示例:使用 -Dhelp 选项列出链接程序调试辅助选项:


demo% ld -Dhelp
      …
debug: args           display input argument processing
debug: bindings       display symbol binding;
debug: detail         provide more information
debug: entry          display entrance criteria descriptors
      …
demo%

例如,-Dfiles 链接程序选项会列出链接过程中引用的所有文件和库:


demo% setenv LD_OPTIONS ’-Dfiles’
demo% f95 direct.f
direct.f:
 MAIN direct:
debug: file=/opt/SUNWspro/lib/crti.o  [ ET_REL ]
debug: file=/opt/SUNWspro/lib/crt1.o  [ ET_REL ]
debug: file=/opt/SUNWspro/lib/values–xi.o  [ ET_REL ]
debug: file=direct.o  [ ET_REL ]
debug: file=/opt/SUNWspro/lib/libM77.a  [ archive ]
debug: file=/opt/SUNWspro/lib/libF77.so  [ ET_DYN ]
debug: file=/opt/SUNWspro/lib/libsunmath.a  [ archive ]
      …

有关这些链接程序选项的更为详细的信息,请参见《链接程序和库指南》。

4.2.3 一致编译和链接

每当分步完成编译和链接时,确保编译和链接选项的一致选择至关重要。在使用选项编译程序的任何部分时,必须使用相同的选项进行链接。另外,许多选项要求使用该选项编译所有源文件,包括链接步骤。

Fortran 用户指南》中的选项说明具体指出了此类选项。

示例:用 -fast 编译 sbr.f,编译 C 例程,然后分步进行链接:


demo% f95 -c -fast sbr.f
demo% cc -c -fast simm.c
demo% f95 -fast sbr.o simm.o        link step; passes  -fast  to the linker

4.3 设置库搜索路径和顺序

链接程序按某一规定顺序在若干位置搜索库。这些位置中有一些是标准路径,有一些则取决于编译器选项 -Rpath-llibrary-Ldir 以及环境变量 LD_LIBRARY_PATH

4.3.1 标准库路径的搜索顺序

链接程序所用的标准库搜索路径由安装路径确定,对于静态和动态加载,它们会有所不同。标准安装将 Sun Studio 编译器软件置于 /opt/SUNWspro/ 下。

4.3.1.1 静态链接

生成可执行文件时,静态链接程序按指定顺序在以下路径(在其他路径中)中搜索 库:

/opt/SUNWspro/lib

Sun Studio 共享库 

/usr/ccs/lib/

SVr4 软件的标准位置 

/usr/lib

UNIX 软件的标准位置 

这些是链接程序所用的缺省路径。

4.3.1.2 动态链接

动态链接程序在运行时按指定顺序搜索共享库:

这些搜索路径被内置于可执行文件中。

4.3.2 LD_LIBRARY_PATH 环境变量

使用 LD_LIBRARY_PATH 环境变量指定链接程序应在哪些目录路径中搜索用 -llibrary 选项指定的库。

可以指定多个目录,其间用冒号分隔。通常,LD_LIBRARY_PATH 变量包含两个用冒号分隔的目录列表,列表间用分号隔开:

dirlist1;dirlist2

首先搜索 dirlist1 中的目录,接着是命令行上用任何显式 -Ldir 指定的目录,再接着是 dirlist2 以及标准目录。

也就是说,如果使用多个 -L 调用编译器,如下所示:

f95 ... -Lpath1 ... -Lpathn ...

则搜索顺序是:

dirlist1 path1 ... pathn dirlist2 standard_paths

LD_LIBRARY_PATH 变量只包含一个用冒号分隔的目录列表时,它会被解释为 dirlist2

在 Solaris 操作环境中,当搜索 64 位依赖性时,可以用相似的环境变量 LD_LIBRARY_PATH_64 来替代 LD_LIBRARY_PATH。有关详细信息,请参见 Solaris《链接程序和库指南》以及 ld(1) 手册页。

4.3.3 库搜索路径和顺序-静态链接

使用 -llibrary 编译器选项对链接程序在解析外部引用时要搜索的其他库命名。例如,用选项 -lmylib 将库 libmylib.solibmylib.a 添加到搜索列表中。

链接程序会在标准目录路径中查找其他的 libmylib 库。-L 选项(和 LD_LIBRARY_PATH 环境变量)会创建一个路径列表,告知链接程序到哪里查找位于标准路径以外的库。

假如 libmylib.a 位于 /home/proj/libs 目录中,则选项 –L/home/proj/libs 会告知链接程序在生成可执行文件时到哪里查找:


demo% f95 -o pgram part1.o part2.o -L/home/proj/libs -lmylib

4.3.3.1 -l library 选项的命令行顺序

对于任何未解析的特殊引用,只对库进行一次搜索,并且只搜索在搜索时未定义的符号。如果命令行上列出了多个库,则会按其在命令行上出现的顺序来搜索这些库。-llibrary 选项放置在以下位置:

4.3.3.2 -Ldir 选项的命令行顺序

-Ldir 选项会将 dir 目录路径添加到库搜索列表中。链接程序首先在 -L 选项指定的任何目录中搜索库,然后在标准目录中进行搜索。只有将其放在它所应用的 –llibrary 选项之前,该选项才有用。

4.3.4 库搜索路径和顺序-动态链接

对于动态库,库搜索路径和加载顺序的更改与静态情况不同。实际链接发生在运行时而不是生成时。

4.3.4.1 在生成时指定动态库

生成可执行文件时,链接程序会在可执行文件本身中记录共享库的路径。这些搜索路径可以用 -Rpath 选项指定。这一点与 -Ldir 选项相反,该选项在生成时指示到哪里查找 -llibrary 选项所指定的库,但不会将该路径记录到二进制可执行文件中。

使用 dump 命令可以查看创建可执行文件时内置的目录路径。

示例:列出内置于 a.out 之中的目录路径:


demo% f95 program.f -R/home/proj/libs -L/home/proj/libs -lmylib
demo% dump -Lv a.out | grep RPATH
[5]      RPATH    /home/proj/libs:/opt/SUNWspro/lib

4.3.4.2 在运行时指定动态库

运行时,链接程序会确定到哪里查找可执行文件所需的动态库:

如前所述,使用 LD_LIBRARY_PATH 会带来意想不到的副作用,因而不建议这样做。

4.3.4.3 修复动态链接期间的错误

当动态链接程序找不到所需库的位置时,它会发出以下错误消息:


ld.so: prog: fatal: libmylib.so: can’t open file:

此消息表明这些库不在其应在的位置。您也许在生成可执行文件时指定了共享库的路径,但这些库随后已被移动。例如,您可能先用 /my/libs/ 中您自己的动态库生成了a.out,而后来又将这些库移到了另一目录。

使用 ldd 确定可执行文件期望在哪儿找到这些库:


demo% ldd a.out
libfui.so.1 =>   /opt/SUNWspro/lib/libfui.so.1
    libfai.so.1 =>   /opt/SUNWspro/lib/libfai.so.1
    libfai2.so.1 =>  /opt/SUNWspro/lib/libfai2.so.1
    libfsumai.so.1 =>    /opt/SUNWspro/lib/libfsumai.so.1
    libfprodai.so.1 =>   /opt/SUNWspro/lib/libfprodai.so.1
    libfminlai.so.1 =>   /opt/SUNWspro/lib/libfminlai.so.1
    libfmaxlai.so.1 =>   /opt/SUNWspro/lib/libfmaxlai.so.1
    libfminvai.so.1 =>   /opt/SUNWspro/lib/libfminvai.so.1
    libfmaxvai.so.1 =>   /opt/SUNWspro/lib/libfmaxvai.so.1
    libfsu.so.1 =>   /opt/SUNWspro/lib/libfsu.so.1
    libsunmath.so.1 =>   /opt/SUNWspro/lib/libsunmath.so.1
    libm.so.1 =>     /usr/lib/libm.so.1
    libc.so.1 =>     /usr/lib/libc.so.1
    libdl.so.1 =>    /usr/lib/libdl.so.1
    /usr/platform/SUNW,Ultra-5_10/lib/libc_psr.so.1

如果可能的话,将这些库移动或复制到正确的目录中,或者在链接程序搜索的目录中建立到该目录的软链接(使用 ln -s)。或者,可能是没有正确设置 LD_LIBRARY_PATH。检查 LD_LIBRARY_PATH 是否包含运行时所需库的路径。

4.4 创建静态库

静态库文件是使用ar(1) 实用程序由预编译的目标文件(.o 文件)生成的。

链接程序从库中提取在当前链接的程序内引用了其入口点的任何元素,如子程序、入口名或 COMMON 子程序中已初始化的 BLOCKDATA 块。这些提取出来的元素(例程)会被永久绑定到链接程序生成的 a.out 可执行文件中。

4.4.1 权衡静态库

与动态情况相比,关于静态库和链接,有三个主要问题需要谨记:

示例:如果 Fortran 程序在两个文件(main.fcrunch.f)中,并且只有后者访问某个库,则在 crunch.fcrunch.o 之前引用该库是错误的:


demo% f95 main.f -lmylibrary crunch.f -o myprog

(不正确)


demo% f95 main.f crunch.f -lmylibrary -o myprog    

(正确)

4.4.2 简单静态库的创建

假设您可以将程序中的所有例程分布在一组源文件中,同时假定这些文件全部包含在子目录 test_lib/ 中。

进一步假定这些文件是以这样一种方式组织的:它们每一个都只包含一个用户程序将会调用的主要子程序,同时还包含该子程序可能会调用的任何“帮助程序”例程,但这些例程不会从库中的任何其他例程中调用。另外,从一个以上库例程中调用的任何帮助程序例程均被集合到单个源文件中。这样就给出了一个组织得非常合理的源文件及目标文件集。

假定每个源文件的名称均取自文件中第一个例程的名称,在多数情况下,该例程是库中的主要文件之一:


demo% cd test_lib
demo% ls
total 14          2 dropx.f      2 evalx.f      2 markx.f
   2 delte.f      2 etc.f        2 linkz.f      2 point.f

更低级的“帮助程序”例程被集中到文件 etc.f 中。其他文件可以包含一个或多个子程序。

首先,使用 -c 选项编译每一个库源文件,生成相应的可重定位 .o 文件:


demo% f95 -c *.f
demo% ls
total 42
 2 dropx.f     4 etc.o      2 linkz.f    4 markx.o 
 2 delte.f     4 dropx.o    2 evalx.f    4 linkz.o     2 point.f
 4 delte.o     2 etc.f      4 evalx.o    2 markx.f     4 point.o
demo%

现在,使用 ar 创建静态库 testlib.a


demo% ar cr testlib.a *.o

要使用该库,可在编译命令中包括此库文件,或者使用 -l-L 编译选项。以下示例直接使用 .a 文件:


demo% cat trylib.f
C    program to test testlib routines
            x=21.998
            call evalx(x)
            call point(x)
            print*, ’value ’,x
            end
demo% f95 -o trylib trylib.f test_lib/testlib.a
demo%

注意,主程序只调用库中的两个例程。您可以验证并未将库中未调用的例程装入可执行文件,方法是查找用 nm 显示的可执行文件的名称列表中是否有这些例程。


demo% nm trylib | grep FUNC | grep point
[146]      |     70016|     152|FUNC |GLOB |0    |8      |point_
demo% nm trylib | grep FUNC | grep evalx
[165]      |     69848|     152|FUNC |GLOB |0    |8      |evalx_
demo% nm trylib | grep FUNC | grep delte
demo% nm trylib | grep FUNC | grep markx
demo% ..etc

在上述示例中,grep 只在名称列表中查找与实际调用的那些库例程相应的项。

引用库的另一方法是通过 -llibrary-Lpath 选项。这里,必须更改库的名称以符合 libname.a 惯例:


demo% mv test_lib/testlib.a test_lib/libtestlib.a
demo% f95 -o trylib trylib.f -Ltest_lib -ltestlib

-llibrary-Lpath 选项与安装在系统公共访问目录(如 /usr/local/lib)中的库一起使用,以便其他用户可以引用它。例如,假如将 libtestlib.a 置于 /usr/local/lib 中,可以通知其他用户使用以下命令编译:


demo% f95 -o myprog myprog.f -L/usr/local/lib -ltestlib

4.4.2.1 静态库中的替换

如果仅有几个元素需要重新编译,没有必要重新编译整个库。ar-r 选项允许替换静态库中的个别元素。

示例:重新编译并替换静态库中的单个例程:


demo% f95 -c point.f
demo% ar -r testlib.a point.o

4.4.2.2 对静态库中的例程进行排序

要在 ar 正在生成静态库时对其中的元素进行排序,请使用命令 lorder(1) 和 tsort(1):


demo% ar -cr mylib.a ’lorder exg.o fofx.o diffz.o | tsort’

4.5 创建动态库

动态库文件是由链接程序 ld 自预编译目标模块生成的,这些模块可在执行开始之后绑定到可执行文件。

动态库的另一功能是模块可供系统中其他正在执行的程序使用,而无需在每个程序的内存中复制模块。鉴于此原因,动态库也是一个共享库。

动态库提供下列功能:

4.5.1 权衡动态库

动态库引入了其他一些权衡考虑因素:

各程序间的性能特征随程序的不同会有很大变化。并非总能预先判断或估计动态库与静态库相比性能会提高(还是降低)。但是,如果所需库的这两种形式对您都可用,则分别评估一下程序使用每种库时的性能还是很值得的。

4.5.2 位置无关代码和 –xcode

可以将位置无关代码 (position-independent code, PIC) 绑定到程序中的任何地址,而无需链接编辑器进行重新定位。从固有性质出发,此类代码可以在同时发生的进程间共享。因而,如果要生成动态共享库,必须使用 -xcode 编译器选项将组件例程编译成与位置无关。

在与位置无关的代码中,对全局项的每一引用均会通过全局偏移表中的指针编译为某一引用。每个函数调用均会通过过程链接表以相对编址模式进行编译。在 SPARC 处理器上,全局偏移表的大小限制为 8 K 字节。

编译器标志 -xcode=v 用于指定二进制对象的代码地址空间。使用该标志,不但可以生成 32、44 或 64 位绝对地址,而且可生成与位置无关的、大小不同的模型代码。(-xcode=pic13 等效于传统的 -pic 标志,-xcode=pic32 等效于 -PIC。)

-xcode=pic32 编译器选项与 -xcode=pic13 类似,但前者允许全局偏移表跨 32 位地址范围。有关详细信息,请参见 f95(1) 手册页或《Fortran 用户指南》。

4.5.3 绑定选项

可以在编译时指定动态或静态库绑定。这些选项实际上是链接程序选项,但它们是由编译器识别并传递给链接程序的。

4.5.3.1 –Bdynamic | -Bstatic

–Bdynamic 用于在各种可能的情况下为共享动态绑定设置首选项。-Bstatic 将绑定只限制于静态库。

当库的静态和动态版本都可用时,使用该选项在命令行首选项间进行切换:

f95 prog.f -Bdynamic -lwells -Bstatic -lsurface

4.5.3.2 –dy | -dn

允许或不允许对整个可执行文件进行动态链接。(该选项只能在命令行上出现一次。)

–dy 允许链接动态共享库。-dn 不允许链接动态库。

4.5.3.3 64 位环境中的绑定

某些静态系统库(如 libm.alibc.a)不能在 64 位 Solaris 操作环境中使用。这些库只作为动态库提供。在这些环境中使用 -dn 将会导致错误,指示缺少某些静态系统库。另外,如果编译器命令行以 -Bstatic 结尾,其结果将是一样的。

要与特定库的静态版本进行链接,请使用类似下面的命令行:

f95 -o prog prog.f -Bstatic -labc -lxyz -Bdynamic

在此,链接的是用户的 libabc.alibxyz.a 文件(而不是 libabc.solibxyz.so),最后的 -Bdynamic 确保以动态方式链接包括系统库在内的其余各库。

在更复杂的情况下,可能必须在链接阶段根据需要用相应的 -Bstatic-Bdynamic 显式引用每个系统库和用户库。首先使用设置为 ’-Dfiles’LD_OPTIONS 获取全部所需库的列表。然后用 -nolib 执行链接步骤(禁止自动链接系统库)并显式引用所需的库。例如:

f95 -m64 -o cdf -nolib cdf.o -Bstatic -lsunmath \ -Bdynamic -lm -lc

4.5.4 命名惯例

为符合链接加载程序和编译器假定的动态库命名惯例,请为您使用前缀 lib 和后缀 .so 创建的动态库命名。例如,编译器选项 -lmyfavs 可以引用 libmyfavs.so

链接程序还接受可选的版本号后缀:例如,libmyfavs.so.1 代表库的第版。

编译器的 -hname 选项将 name 记录为正在生成的动态库的名称。

4.5.5 一个简单动态库

生成动态库需要用 -xcode 选项和链接程序选项 -G-ztext-hname 编译源文件。这些链接程序选项可通过编译器命令行来提供。

您可以用静态库示例中使用的相同文件创建一个动态库。

示例:用 -pic 和其他链接程序选项编译:


demo% f95 -o libtestlib.so.1 -G -xcode=pic13 -ztext  \ 
–hlibtestlib.so.1 *.f

–G 告知链接程序生成一个动态库。

–ztext 会在发现与位置无关的代码以外的任何内容(如可重定位文本)时发出警告。

示例:使用动态库生成可执行文件 a.out


demo% f95 -o trylib -R”pwd” trylib.f libtestlib.so.1
demo% file trylib
trylib:ELF 32–bit MSB executable SPARC Version 1, dynamically linked, not stripped
demo% ldd trylib
     libtestlib.so.1 => /export/home/U/Tests/libtestlib.so.1
     libfui.so.1 => /opt/SUNWspro/lib/libfui.so.1
     libfai.so.1 => /opt/SUNWspro/lib/libfai.so.1
     libc.so.1 => /usr/lib/libc.so.1

注意,此示例使用 -R 选项将动态库路径(当前目录)绑定到可执行文件中。

file 命令显示可执行文件是以动态方式链接的。

4.5.6 初始化公共块

生成动态库时,通过将已初始化的公共块集中到同一库中并在其他所有库之前引用该库,可确保正确初始化公共块(用 DATABLOCK DATA 表示的块)。

例如:


demo% f95 -G -xcode=pic32 -o init.so blkdat1.f blkdat2.f blkdat3.f
demo% f95 -o prog main.f init.so otherlib1.so otherlib2.so

首次编译会由定义公共块并在 BLOCK DATA 单元中对其进行初始化的文件创建一个动态库。第二次编译创建可执行二进制文件,将已编译的主程序与应用程序所需的动态库链接起来。注意,初始化所有公共块的动态库在其他所有库之前首先出现。这样将确保正确地初始化这些块。

4.6 随 Sun Fortran 编译器提供的库

下表展示了随编译器一同安装的库。

表 4–1 随编译器提供的主要库

库 

名称 

所需选项 

f95 内在支持

libfsu

无 

f95 接口

libfui

无 

f95 数组内在库

libf*ai

无 

f95 区间运算内在库

libifai

-xinterval

Sun 数学函数库 

libsunmath

无 

4.7 可发送库

如果您的可执行文件使用了 runtime.libraries 自述文件中列出的某个 Sun 动态库,则您的许可证包括将该库重新分发给客户的权利

该自述文件位于 Sun Studio SDN 门户网站:

http://developers.sun.com/sunstudio/documentation/ss12/

请勿以任何形式重新分发或透露头文件、源代码、目标模块或目标模块的静态库。

有关更多详细信息,请参阅您的软件许可证。