在读取了所有输入文件并完成了所有符号解析后,链接编辑器将搜索内部符号表,以查找尚未绑定到符号定义的任何符号引用。这些符号引用称为未定义符号。未定义符号对链接编辑过程的影响因要生成的输出文件类型以及符号类型而异。
生成可执行输出文件时,如果有任何未定义符号,则链接编辑器的缺省行为是终止并显示相应的错误消息。如果可重定位目标文件中的符号引用从未与符号定义匹配,则符号将保持未定义状态。
$ cat main.c extern int foo(); int main() { return (foo()); } $ cc -o prog main.c Undefined first referenced symbol in file foo main.o ld: fatal: symbol referencing errors
同样,如果使用共享目标文件创建动态可执行文件,并且该共享目标文件中包含未解析的符号定义,则会产生未定义符号错误。
$ cat foo.c extern int bar; int foo() { return (bar); } $ cc -o libfoo.so -G -K pic foo.c $ cc -o prog main.c -L. -lfoo Undefined first referenced symbol in file bar ./libfoo.so ld: fatal: symbol referencing errors
要像在上个示例中一样,允许未定义的符号,可使用链接编辑器的 –z nodefs 选项抑制缺省错误状态。
将可重定位目标文件中的符号引用绑定到隐式定义的共享目标文件中的符号定义时,符号也可能保持未定义状态。例如,继续使用上一个示例中使用的 main.c 和 foo.c 文件。
$ cat bar.c int bar = 1; $ cc -o libbar.so -R. -G -K pic bar.c -L. -lfoo $ ldd libbar.so libfoo.so => ./libfoo.so $ cc -o prog main.c -L. -lbar Undefined first referenced symbol in file foo main.o (symbol belongs to implicit \ dependency ./libfoo.so) ld: fatal: symbol referencing errors
prog 是通过显式引用 libbar.so 而生成的。libbar.so 依赖于 libfoo.so。因此,就建立了从 prog 到 libfoo.so 的隐式引用。
因为 main.c 对 libfoo.so 提供的接口进行特定引用,所以 prog 确实依赖于 libfoo.so。但是,在要生成的输出文件中将仅记录显式共享目标文件的依赖项。因此,如果开发了一个不再依赖于 libfoo.so 的新版本 libbar.so,prog 将无法运行。
因此,此类型的绑定被认为是致命错误。必须通过在链接编辑 prog 期间直接引用库来使隐式引用变为显式引用。前面示例中显示的致命错误消息中会提示需要的引用。
链接编辑器在生成共享目标文件输出文件时,允许在链接编辑结束时仍存在未定义符号。此缺省行为允许共享目标文件从将其定义为依赖项的动态可执行文件导入符号。
可以使用链接编辑器的 –z defs 选项在存在任何未定义符号的情况下强制生成致命错误。建议在创建任何共享目标文件时使用此选项。引用了某个应用程序中的符号的共享目标文件可以在使用 extern mapfile 指令定义符号的同时使用 –z defs 选项。请参见SYMBOL_SCOPE / SYMBOL_VERSION 指令。
自包含的共享目标文件(其中的所有对外部符号的引用都通过指定的依赖项得到满足)可提供最大的灵活性。此共享目标文件可由许多用户使用,并且这些用户无需确定和建立依赖项来满足共享目标文件的要求。
以前,使用弱符号来禁用插入,或对可选功能进行测试。但是,经验表明,弱符号在当前编程环境中易变化且不可靠,建议不要使用它们。
在系统共享目标文件中经常使用弱符号别名。目的在于提供替代接口名称,通常符号名称使用 "_" 字符作为前缀。可从其他系统共享目标文件中引用该别名,以避免由于应用程序导出其自己的符号名称实现而导致的插入问题。在实践中,证明了该技术过于复杂且使用不一致。Oracle Solaris 的最新版本可在使用直接绑定的系统目标文件之间建立显式绑定。请参见Chapter 6, 直接绑定。
经常使用弱符号引用,以在运行时测试接口是否存在。该技术对生成环境、运行时环境进行限制,可通过编译器优化禁用该技术。使用具有 RTLD_DEFAULT 或 RTLD_PROBE 句柄的 dlsym(3C),可提供一致、稳健的方式来测试符号是否存在。请参见测试功能。