0. 背景
在测试 protobuf-c 编码时,由于已编译安装好 protobuf-c 相关的库,简单写了一个例子进行测试。
直接使用gcc命令进行编译时,报出如下错误:
gcc `pkg-config --cflags --libs libprotobuf-c` -o test *.c ../test.pb-c.c -I.. /usr/bin/ld: /tmp/cc2Zocqz.o: in function `main': test-generated-code.c:(.text+0xe7): undefined reference to `protobuf_c_buffer_simple_append' ...
1. ld参数顺序
从错误日志中可以明显看出,错误是由 ld 报出,也就是link时发生了搜寻不到相关定义,也就是没能够成功链接到 protobuf-c 相关的库。
直接运行 pkg-config --cflags --libs libprotobuf-c ,返回 -I/usr/local/include -L/usr/local/lib -lprotobuf-c ,表明 pkg-config 工具能够能够正确返回配置信息。
那么问题应该还是出在 ld 命令上,键入 man ld ,获取帮助信息。
查看 -I 、 -L 、 -l 、 -o 等参数的介绍,发现了如下解释:
-l namespec
--library=namespec
Add the archive or object file specified by namespec to the list of files to link. This option may be used any number
of times. If namespec is of the form :filename, ld will search the
library path for a file called filename, otherwise it will search the library path for a file called libnamespec.a.
On systems which support shared libraries, ld may also search for files other than libnamespec.a. Specifically, on ELF
and SunOS systems, ld will search a directory for a library called libnamespec.so before searching for one called
libnamespec.a. (By convention, a ".so" extension indicates a shared library.) Note that this behavior does not apply
to :filename, which always specifies a file called filename.
The linker will search an archive only once, at the location where it is specified on the command line. If the archive
defines a symbol which was undefined in some object which appeared before the archive on the command line, the linker
will include the appropriate file(s) from the archive. However, an undefined symbol in an object appearing later on the
command line will not cause the linker to search the archive again.
See the -( option for a way to force the linker to search archives multiple times.
You may list the same archive multiple times on the command line.
This type of archive searching is standard for Unix linkers. However, if you are using ld on AIX, note that it is
different from the behaviour of the AIX linker.
Google翻译就是:
-l名称规范
--library = namespec
将namespec指定的归档文件或目标文件添加到要链接的文件列表中。此选项可以使用多次。如果namespec的格式为:filename,则ld将在库路径中搜索
名为filename的文件,否则将在库路径中搜索名为libnamespec.a的文件。
在支持共享库的系统上,ld可能还会搜索libnamespec.a以外的文件。具体来说,在ELF和SunOS系统上,ld将在目录中搜索名为libnamespec.so的库,
然后再搜索一个名为libnamespec.a的库。 (按照惯例,扩展名“ .so”表示共享库。)请注意,此行为不适用于:filename,它始终指定一个名为
filename的文件。
链接器仅在命令行上指定的位置搜索一次存档。如果归档文件定义了在命令行上归档文件之前出现的某个对象中未定义的符号,则链接器将包含归档文件中
的相应文件。但是,稍后出现在命令行中的对象中未定义的符号将不会导致链接程序再次搜索档案。
请参阅-(选项,以强制链接程序多次搜索存档。
您可以在命令行上多次列出同一档案。
这种类型的档案搜索是Unix链接器的标准配置。但是,如果在AIX上使用ld,请注意它与AIX链接器的行为不同。
重点信息,我已下划线标出,意思就是 ld 链接符号时仅搜寻一次,且仅链接 -l 前面的代码中的符号。
2. 问题修正
知道问题产生的原因,那么修正就容易多了,我们只需要将 pkg-config --cflags --libs libprotobuf-c 放在命令的后面就可以了。
执行下面的命令,能够顺利编译成果物。
gcc -o test *.c ../test.pb-c.c -I.. `pkg-config --cflags --libs libprotobuf-c`
想了解更多链接器与库的知识,可以看看此文件Oracle® Solaris 11.1 Linkers and Libraries Guide。
3. 一些思考
- 平时习惯于借助 CMake 构建工程,对 CMake 语法及函数接口比较熟悉,然而并不了解其内部详细的工作机制,也对编译时输出的log“视而不见”。
- CMake 工具的意义是帮助我们快速方便地构建复杂的工程,但是 make 、 configure 等编译方法,我们也应当了解和掌握。
- 对于遇到的问题,应当保持严肃谨慎的态度,大胆假设,小心求证。