undefined symbol 問題解決記錄

2020-08-12 11:46:06

歷經一個月,昨日完成印表機network部分的編寫(c語言),編寫makefile構建動態庫。構建完成後遂進行呼叫測試,出現:

./network: symbol lookup error: /usr/lib64/netPrnctl.so: undefined symbol: cupsGetDests

將解決方法與過程記錄,以便日後查閱。

查詢與分析原因

在編譯時未出現問題,沒有報錯,成功編譯生成動態庫。以下爲編譯的makefile檔案。

###########################################################
#	File:	netPrnctl.so Makefile
#	Author: Neko
###########################################################

CC = gcc
CFLAGS = -Wall -g -fPIC

INCLUDE = -I./inc -I../ -I../cups -I../backend -I/usr/include/libusb-1.0
TARGET = netPrnctl.so

vpath % .h ./inc

OBJS	= public.o prnctlAvision.o prnctlNetwork.o prnctl.o
SRCS	= ./src/public.c ./src/prnctlAvision.c ./src/prnctlNetwork.c ./src/prnctl.c

$(OBJS):$(SRCS)
	$(CC) $(CFLAGS) $(INCLUDE) -c $^

all:$(OBJS)
	$(CC) -shared -fPIC -o $(TARGET) $(OBJS) -pthread
	cp $(TARGET) /usr/bin
	cp $(TARGET) /usr/lib64

clean:
	rm -f *.o
	rm -f netPrnctl.so

雖成功生成了動態庫,但是在呼叫時卻出現找不到符號的問題,於是使用 ldd -r 檢視

neko@neko:~/ZCPrinterDevice/NetworkSetupTool/filter$ ldd -r /usr/lib64/netPrnctl.so
	linux-vdso.so.1 (0x00007ffeef44c000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9607add000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f96076ec000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f9607f0f000)
undefined symbol: libusb_open	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsArrayNew	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsDirOpen	(/usr/lib64/netPrnctl.so)
undefined symbol: ppdFindAttr	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsParseOptions	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsGetPPD	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_set_interface_alt_setting	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_release_interface	(/usr/lib64/netPrnctl.so)
undefined symbol: ppdClose	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsGetOption	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_detach_kernel_driver	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_close	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsFreeOptions	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsDirClose	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_get_string_descriptor_ascii	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_free_config_descriptor	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_get_config_descriptor	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsBackChannelWrite	(/usr/lib64/netPrnctl.so)
undefined symbol: _cupsGet1284Values	(/usr/lib64/netPrnctl.so)
undefined symbol: _ppdNormalizeMakeAndModel	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_attach_kernel_driver	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_get_device_list	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_kernel_driver_active	(/usr/lib64/netPrnctl.so)
undefined symbol: _cups_strcasecmp	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_bulk_transfer	(/usr/lib64/netPrnctl.so)
undefined symbol: _cupsLangPrintFilter	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsSideChannelRead	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsArrayCount	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsFileClose	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsDirRead	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_set_configuration	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsFreeDests	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_open_device_with_vid_pid	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_reset_device	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_exit	(/usr/lib64/netPrnctl.so)
undefined symbol: ppdOpenFile	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_init	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_get_device_descriptor	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsBackendReport	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_free_device_list	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_claim_interface	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsFileOpen	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_control_transfer	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsFileGets	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsArrayAdd	(/usr/lib64/netPrnctl.so)
undefined symbol: backendGetMakeModel	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsArrayFind	(/usr/lib64/netPrnctl.so)
undefined symbol: _cups_strlcpy	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsGetDests	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsSideChannelWrite	(/usr/lib64/netPrnctl.so)
undefined symbol: httpAssembleURIf	(/usr/lib64/netPrnctl.so)

發現動態庫沒有鏈接libusb 與 libcups 兩個庫,所以netPrnctl.so用到的這兩個的函數都沒有符號。
雖然構建netPrnctl.so的c檔案已包含libusb與cups函數的標頭檔案,編譯器做的工作僅是將原始檔中以「文字形式」存在的原始碼翻譯成機器語言(二進制)的形式,並生成目標檔案(原始碼全部變成"二進制"的形式),在此期間對原始碼中的語法進行檢查,有語法錯誤則報錯,程式無法通過編譯。故編譯沒有錯誤僅是說明沒有語法上的問題。在實際呼叫時也可能出現其他情況。
通過上面的makefile看到,編譯生成了
OBJS = public.o prnctlAvision.o prnctlNetwork.o prnctl.o
這4個目標檔案。
而當鏈接器進行鏈接的時候,會先把各個目標檔案在可執行檔案(ELF檔案的一種,共用庫也是屬於其中)裏面的位置確定下來,然後經過一系列處理,直到把所有目標檔案的內容都寫在可執行檔案中對應的位置上,再經過一些工作,就會生成一個可執行程式。
由此判斷,netPrnctl.so依賴的libusb 與 libcups 兩個庫沒有鏈接上。

解決辦法

既然是因爲沒有鏈接libusb 與 libcups,那麼將這兩個庫鏈接上是不是就能解決問題?
首先檢視這兩個庫的路徑。一般gcc裡會有。

neko@neko:~/ZCPrinterDevice/NetworkSetupTool/filter$ ls /usr/lib/x86_64-linux-gnu/ | grep libcups
libcupscgi.so.1
libcupsfilters.so.1
libcupsfilters.so.1.0.0
libcupsimage.so.2
libcupsmime.so.1
libcupsppdc.so.1
libcups.so.2
neko@neko:~/ZCPrinterDevice/NetworkSetupTool/filter$ ls /usr/lib/x86_64-linux-gnu/ | grep libusb
libusb-1.0.a
libusb-1.0.so
libusbmuxd.so.4
libusbmuxd.so.4.0.0

於是將makefile進行調整,新增鏈接libusb 與 libcups的語句。

###########################################################
#	File:	netPrnctl.so Makefile
#	Author: Neko
###########################################################

CC = gcc
CFLAGS = -Wall -g -fPIC

INCLUDE = -I./inc -I../ -I../cups -I../backend -I/usr/include/libusb-1.0
TARGET = netPrnctl.so
LIBVAR = -lusb-1.0 -lcups
LIBPATH = -L/usr/lib64/x86_64-linux-gnu

vpath % .h ./inc

OBJS	= public.o prnctlAvision.o prnctlNetwork.o prnctl.o
SRCS	= ./src/public.c ./src/prnctlAvision.c ./src/prnctlNetwork.c ./src/prnctl.c

$(OBJS):$(SRCS)
	$(CC) $(CFLAGS) $(INCLUDE) -c $^

all:$(OBJS)
	$(CC) -shared -fPIC -o $(TARGET) $(OBJS) $(LIBPATH) $(LIBVAR) -pthread
	cp $(TARGET) /usr/bin
	cp $(TARGET) /usr/lib64

clean:
	rm -f *.o
	rm -f netPrnctl.so

編譯時卻報錯

/usr/bin/ld: 找不到 -lcups

這裏說一下:
出現 /usr/bin/ld: cannot find(找不到) -lxxx
xxx表示函式庫檔名稱,如:libc.so、libltdl.so、libXtst.so。

命名規則是:lib+庫名(即xxx)+.so。

會發生這樣的原因有以下三種情形:

1 系統沒有安裝相對應的lib

2 相對應的lib版本不對

3 lib(.so檔)的symbolic link不正確,沒有連結到正確的函式庫檔案(.so)

而libcups.so.2不符合命名規則。故報錯找不到。
解決辦法:
到libcups所在路徑下,鏈接新的符合規則的libcups.so

neko@neko:~/ZCPrinterDevice/NetworkSetupTool/filter$ cd  /usr/lib/x86_64-linux-gnu
neko@neko:/usr/lib/x86_64-linux-gnu$ sudo ln -s libcups.so.2 libcups.so
neko@neko:/usr/lib/x86_64-linux-gnu$ cd -
/home/neko/ZCPrinterDevice/NetworkSetupTool/filter
neko@neko:~/ZCPrinterDevice/NetworkSetupTool/filter$ ls /usr/lib/x86_64-linux-gnu/ | grep libcups
libcupscgi.so.1
libcupsfilters.so.1
libcupsfilters.so.1.0.0
libcupsimage.so.2
libcupsmime.so.1
libcupsppdc.so.1
libcups.so
libcups.so.2

重新編譯成功,沒有報錯。再次呼叫測試,也沒有出現找不到符號的問題。

neko@neko:~/ZCPrinterDevice/NetworkSetupTool/filter$ ldd -r /usr/lib64/netPrnctl.so
	linux-vdso.so.1 (0x00007fff43f66000)
	libusb-1.0.so.0 => /lib/x86_64-linux-gnu/libusb-1.0.so.0 (0x00007f4087f39000)
	libcups.so.2 => /usr/lib/x86_64-linux-gnu/libcups.so.2 (0x00007f4087cad000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f4087a8e000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f408769d000)
	libudev.so.1 => /lib/x86_64-linux-gnu/libudev.so.1 (0x00007f408747f000)
	libgssapi_krb5.so.2 => /usr/lib/x86_64-linux-gnu/libgssapi_krb5.so.2 (0x00007f4087234000)
	libgnutls.so.30 => /usr/lib/x86_64-linux-gnu/libgnutls.so.30 (0x00007f4086ecf000)
	libavahi-common.so.3 => /usr/lib/x86_64-linux-gnu/libavahi-common.so.3 (0x00007f4086cc3000)
	libavahi-client.so.3 => /usr/lib/x86_64-linux-gnu/libavahi-client.so.3 (0x00007f4086ab2000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f4086895000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f40864f7000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f4088364000)
	librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f40862ef000)
	libkrb5.so.3 => /usr/lib/x86_64-linux-gnu/libkrb5.so.3 (0x00007f4086019000)
	libk5crypto.so.3 => /usr/lib/x86_64-linux-gnu/libk5crypto.so.3 (0x00007f4085de7000)
	libcom_err.so.2 => /lib/x86_64-linux-gnu/libcom_err.so.2 (0x00007f4085be3000)
	libkrb5support.so.0 => /usr/lib/x86_64-linux-gnu/libkrb5support.so.0 (0x00007f40859d8000)
	libp11-kit.so.0 => /usr/lib/x86_64-linux-gnu/libp11-kit.so.0 (0x00007f40856a9000)
	libidn2.so.0 => /usr/lib/x86_64-linux-gnu/libidn2.so.0 (0x00007f408548c000)
	libunistring.so.2 => /usr/lib/x86_64-linux-gnu/libunistring.so.2 (0x00007f408510e000)
	libtasn1.so.6 => /usr/lib/x86_64-linux-gnu/libtasn1.so.6 (0x00007f4084efb000)
	libnettle.so.6 => /usr/lib/x86_64-linux-gnu/libnettle.so.6 (0x00007f4084cc5000)
	libhogweed.so.4 => /usr/lib/x86_64-linux-gnu/libhogweed.so.4 (0x00007f4084a91000)
	libgmp.so.10 => /usr/lib/x86_64-linux-gnu/libgmp.so.10 (0x00007f4084810000)
	libdbus-1.so.3 => /lib/x86_64-linux-gnu/libdbus-1.so.3 (0x00007f40845c3000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f40843bf000)
	libkeyutils.so.1 => /lib/x86_64-linux-gnu/libkeyutils.so.1 (0x00007f40841bb000)
	libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f4083fa0000)
	libffi.so.6 => /usr/lib/x86_64-linux-gnu/libffi.so.6 (0x00007f4083d98000)
	libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007f4083b14000)
	liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007f40838ee000)
	liblz4.so.1 => /usr/lib/x86_64-linux-gnu/liblz4.so.1 (0x00007f40836d2000)
	libgcrypt.so.20 => /lib/x86_64-linux-gnu/libgcrypt.so.20 (0x00007f40833b7000)
	libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007f40831a2000)

可以看到,依賴的庫基本都鏈接上了。

結語

所有錯誤都有原因,有跡可循。遇到問題不要急躁,靜心思考,理出種種可能導致問題的原因,再排除,找到根源去解決。搜尋引擎和書籍是很好的工具,凡事先儘量自己想辦法,獨立解決問題也是一種能力。
參加工作半年,此時的我也只是一個剛進門的初級工程師。這些話是對我自己的告誡,也是對與我一樣在門內探索的同學的建議,希望有所幫助。共勉。