🔞 这个诡异的问题,我必须记下来:
(一)在CentOS下解决过的问题
可以先参考之前的问题:
🔗《Linux编译C++项目找不到libclntsh.so库》
当时的环境是CentOS7,找不到Oracle11客户端的libclntsh.so
库。
经过各种配置和尝试,发现环境变量和ldconfig似乎无效。
最终,在/usr/lib/
目录下,新建文件链接/usr/lib/libclntsh.so
搞定。
(二)在Ubuntu下无法解决
换成Ubuntu20后,
原本在CentOS7下正常编译的项目,又报找不到某某函数。
每个函数都找不到,类似下面:
......
/usr/bin/ld: MyOracle.cpp:(.text+0x1290): undefined reference to `OCIAttrGet'
/usr/bin/ld: MyOracle.cpp:(.text+0x1330): undefined reference to `OCIHandleAlloc'
/usr/bin/ld: MyOracle.cpp:(.text+0x1356): undefined reference to `OCIHandleAlloc'
/usr/bin/ld: MyOracle.cpp:(.text+0x1398): undefined reference to `OCIParamGet'
/usr/bin/ld: MyOracle.cpp:(.text+0x13bf): undefined reference to `OCIAttrGet'
/usr/bin/ld: MyOracle.cpp:(.text+0x141a): undefined reference to `OCIErrorGet'
/usr/bin/ld: MyOracle.cpp:(.text+0x146c): undefined reference to `OCIHandleFree'
/usr/bin/ld: MyOracle.cpp:(.text+0x147b): undefined reference to `OCIHandleFree'
/usr/bin/ld: MyOracle.cpp:(.text+0x14a2): undefined reference to `OCIAttrGet'
/usr/bin/ld: MyOracle.cpp:(.text+0x14e3): undefined reference to `OCIErrorGet'
......
(2.1)之前方法无效
重复之前CentOS7的解决办法,建立了文件链接/usr/lib/libclntsh.so
也无效。
百思不得其解,一直未能解决。
所以很久以来都没在Ubuntu20下编译。
(2.2)新的发现
后来因为数据库改造,增加了PostgreSQL
/MySQL
适配(可参考下面2篇,和本次主题关系不大)。
🔗《业务系统从Oracle迁移到openGauss数据库的简单记录》
🔗《业务系统兼容数据库Oracle/PostgreSQL(openGauss)/MySQL的琐事》
改造完Java
,Pascal
之后轮到C++
了。
在CentOS7下安装对应数据库客户端后,重复之前Oracle的解决办法处理MySQL和PostgreSQL的库,编译成功。
顺手在Ubuntu20下又试了,因为没装客户端。
报错:
......
/usr/bin/ld: 找不到 -lpq
collect2: error: ld returned 1 exit status
......
装完sudo apt install postgresql-client
好像没库。
又装sudo apt install libpq-dev
之后。
报错:
......
/usr/bin/ld: TMEFunction.cpp:(.text+0x6525): undefined reference to `PQconnectdb'
/usr/bin/ld: TMEFunction.cpp:(.text+0x6534): undefined reference to `PQstatus'
/usr/bin/ld: TMEFunction.cpp:(.text+0x654f): undefined reference to `PQexec'
/usr/bin/ld: TMEFunction.cpp:(.text+0x6567): undefined reference to `PQgetvalue'
/usr/bin/ld: TMEFunction.cpp:(.text+0x6598): undefined reference to `PQclear'
......
这不和Oracle一模一样么,根本不是找不到库,而是找不到函数!!!
(2.3)无效的尝试
这时候我才明白,问题不是找不到库,而是找不到函数……
看到有说法需要:
extern "C" {
#include "mysql.h"
#include "libpq-fe.h"
}
原理是:
extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。
加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。
但问题依旧,并没有解决。
(2.4)似乎看懂了的原理
参考 🔗《Why does the order of ‘-l’ option in gcc matter?》
简单说:gcc的参数顺序是重要的。
在CentOS7的g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44)
帮助中,关于-l的描述:
-l library
Search the library named library when linking. (The second alternative with the library as a separate argument is only for POSIX compliance and is not recommended.)
It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified. Thus, foo.o
-lz bar.o searches library z after file foo.o but before bar.o. If bar.o refers to functions in z, those functions may not be loaded.
The linker searches a standard list of directories for the library, which is actually a file named liblibrary.a. The linker then uses this file as if it had been specified
precisely by name.
The directories searched include several standard system directories plus any that you specify with -L.
Normally the files found this way are library files---archive files whose members are object files. The linker handles an archive file by scanning through it for members
which define symbols that have so far been referenced but not defined. But if the file that is found is an ordinary object file, it is linked in the usual fashion. The
only difference between using an -l option and specifying a file name is that -l surrounds library with lib and .a and searches several directories.
在Ubuntu20的g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1)
帮助中,关于-l的描述:
-l library
Search the library named library when linking. (The second alternative with the library as a separate argument is only for POSIX compliance and is not recommended.)
The -l option is passed directly to the linker by GCC. Refer to your linker documentation for exact details. The general description below applies to the GNU linker.
The linker searches a standard list of directories for the library. The directories searched include several standard system directories plus any that you specify with -L.
Static libraries are archives of object files, and have file names like liblibrary.a. Some targets also support shared libraries, which typically have names like
liblibrary.so. If both static and shared libraries are found, the linker gives preference to linking with the shared library unless the -static option is used.
It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified. Thus, foo.o
-lz bar.o searches library z after file foo.o but before bar.o. If bar.o refers to functions in z, those functions may not be loaded.
都有一段(算了还是翻译一下)描述如下:
链接器按照指定的顺序搜索和处理库和目标文件。
因此,foo.o -lz bar.o
将在文件foo.o
之后,在bar.o
之前搜索库libz
。
所以如果bar.o
引用libz
中的函数,则可能不会加载这些函数。
也就是需要命令行中:用到库函数的源码在前,库文件在后。
(2.5)解决办法
就是修改makefile
中链接的指令顺序:
原本:g++ -Wall -lm -lpq -lmysqlclient -lclntsh a1.o a2.o ... xxx.o -o output
❌
…
改为:g++ -Wall -lm a1.o a2.o ... xxx.o -lpq -lmysqlclient -lclntsh -o output
✔️
就解决啦!!!
(三)依然有疑问
虽然解决了在Ubuntu20下编译的问题。
但是依然不理解。
之前的顺序,为什么能在CentOS7下版本的g++通过。
明明两个g++版本关于-l的说明是一致的。
而帮助中的则可能不会加载这些函数
,those functions may not be loaded
,不确定语气也让人嘀咕。