看到 C/C++初學者 FB 社團的 發問,
情境是沒有 header file 下要如何使用 .so 編譯 C++ 的程式。
因為沒有後續,所以也不確定是否解決,不過最後的回答的確是突破口。
我之前有記錄過 “ 107.08.25 C 語言 static 和 shared 函式庫” 不過是純 C 不適用此問題。以下為此問題的解決過程記錄。
TL;DR
問題定義
手邊有 libabc.so, libabc.a,沒有 *.h, *.c
這邊就使用 libabc.so 為主要問題 (因為解決後,libabc.a 可用相同步驟解決)
libabc.so 中有 xyz()
可供呼叫
為了重現問題以下為 libabc 的測試程式 (因為問題中提到用 gcc 與 c 是可以使用,代表該函式庫有使用 extern "C" {...}
):
abc.cpp
:#include <iostream> extern "C" { void xyz() { std::cout << "xyz" << std::endl; } }
- 產生
libabc.so
:$ g++ -c -fPIC -o libabc.o abc.cpp $ g++ -shared -o libabc.so libabc.o && rm libabc.o
C (gcc) 可行版本
test0.c
#include <stdio.h> extern void xyz(); int main(int argc, char *argv[]) { xyz(); return 0; }
- 編譯並執行
$ gcc -o test test0.c -L. -labc -Wl,-rpath=. $ ./test xyz
C++ (g++) 無法鏈結版本
test1.cpp
#include <iostream> extern void xyz(); int main(int argc, char *argv[]) { xyz(); return 0; }
- 編譯
$ g++ -o test test1.cpp -L. -labc -Wl,-rpath=. /tmp/ccL0SPL8.o: In function `main': t1.cpp:(.text+0x10): undefined reference to `xyz()' collect2: error: ld returned 1 exit status
- 找出問題: 可以看到需要
_Z3xyzv
這個 symbol 但是在 linker 鏈結時找不到,因為libabc
所提供的xyz()
有定義extern "C" {...}
所以其 symbol 是xyz
。$ g++ -c test1.cpp $ nm test1.o U _GLOBAL_OFFSET_TABLE_ 0000000000000064 t _GLOBAL__sub_I_main U _Z3xyzv 000000000000001b t _Z41__static_initialization_and_destruction_0ii U _ZNSt8ios_base4InitC1Ev U _ZNSt8ios_base4InitD1Ev 0000000000000000 r _ZStL19piecewise_construct 0000000000000000 b _ZStL8__ioinit U __cxa_atexit U __dso_handle 0000000000000000 T main
C++ (g++) 可行版本
test2.cpp
#include <stdio.h> extern "C" void xyz(); int main(int argc, char *argv[]) { xyz(); return 0; }
- 編譯並執行
$ gcc -o test test2.cpp -L. -labc -Wl,-rpath=. $ ./test xyz
- 確認
$ g++ -c t1.cpp $ nm t1.o U _GLOBAL_OFFSET_TABLE_ 0000000000000064 t _GLOBAL__sub_I_main 000000000000001b t _Z41__static_initialization_and_destruction_0ii U _ZNSt8ios_base4InitC1Ev U _ZNSt8ios_base4InitD1Ev 0000000000000000 r _ZStL19piecewise_construct 0000000000000000 b _ZStL8__ioinit U __cxa_atexit U __dso_handle 0000000000000000 T main U xyz
C++ (g++) dlopen 噁心可行版本
原先我覺得沒有 header file (*.h) 應該無法做到,所以就先繞了遠路用 dlopen 1 解決,後來才想到可以先用 extern
宣告尚未定義的函式。需要注意的是 dlopen 可能會影響效能。
test3.cpp
#include <dlfcn.h> #include <iostream> using func_t = void (*)(); int main(int argc, char *argv[]) { const char *error; auto lib = dlopen("libabc.so", RTLD_LAZY | RTLD_GLOBAL); if ((error = dlerror()) != NULL) { std::cerr << error; exit(EXIT_FAILURE); } func_t xyz = (func_t)dlsym(lib, "xyz"); if ((error = dlerror()) != NULL) { std::cerr << error; exit(EXIT_FAILURE); } xyz(); return EXIT_SUCCESS; }
- 編譯並執行
$ g++ -o test test3.cpp -ldl -Wl,-rpath=. $ ./test xyz