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 等編譯方法,我們也應當了解和掌握。
- 對於遇到的問題,應當保持嚴肅謹慎的態度,大膽假設,小心求證。