class base undefined symbol in c++

ss
5 min readJan 8, 2021

--

這問題很有趣, 今天我們假設這樣定義一個header, 他是一個基底模板,

// A.h
#include <string>
class A
{
public:
A(){};
~A(){};
virtual void invoke();
protected:
std::string name;
};

假設我們之後定義任意一個object, 即可繼承這個上面的模板, 並且需要實作invoke這個function

// B.h

#include "A.h"
#include <iostream>
class B : public A
{
public:
B(){};
~B(){};
void invoke() override
{
std::cout << "I'm B\n";
};
};

最後我們給一個main.cpp

#include "B.h"int main()
{
B b = B();
b.invoke();
return 0;
}

最後我們編譯他

$g++ main.cpp
/tmp/ccK0skET.o: In function `A::A()':
main.cpp:(.text._ZN1AC2Ev[_ZN1AC5Ev]+0xf): undefined reference to `vtable for A'
/tmp/ccK0skET.o: In function `A::~A()':
main.cpp:(.text._ZN1AD2Ev[_ZN1AD5Ev]+0xf): undefined reference to `vtable for A'
/tmp/ccK0skET.o:(.data.rel.ro._ZTI1B[_ZTI1B]+0x10): undefined reference to `typeinfo for A'
collect2: error: ld returned 1 exit status

很高興能在這邊直接看到編譯器提示我們的錯誤, 很明確地告訴我們vtable並沒有參考依據

問題出在 A.h 中, 我們忘記實作virtual function了

只需要實作, 或是將此virtual 設置為 pure virtual fucntion 即可通行

// A.h
#include <string>
class A
{
public:
A(){};
~A(){};
virtual void invoke() = 0;
protected:
std::string name;
};

但問題不僅僅這麼easy, 在某些情況下這樣的行為會被允許?

假設我們今天要做的是動態連結, 下的指令如下

$g++ -fPIC -o libtest.so -shared main.cpp

你可以發現, 順利產生libtest.so 且並沒有任何的副作用, 可是virtual 還是未被reference

於是你開始找到底是哪邊沒有去動態連結到, 但找了一圈發現這跟連結應該無關

我們用nm 這個指令去看

$nm -u libtest.so 
U __cxa_atexit@@GLIBC_2.2.5
w __cxa_finalize@@GLIBC_2.2.5
w __gmon_start__
U __gxx_personality_v0@@CXXABI_1.3
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __stack_chk_fail@@GLIBC_2.4
U _Unwind_Resume@@GCC_3.0
U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev@@GLIBCXX_3.4.21
U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev@@GLIBCXX_3.4.21
U _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4
U _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4
U _ZSt4cout@@GLIBCXX_3.4
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.4
U _ZTI1A
U _ZTV1A
U _ZTVN10__cxxabiv120__si_class_type_infoE@@CXXABI_1.3

可以注意到 跟我們有關的是

U _ZTI1A
U _ZTV1A

我們可以用 c++filt 看一下這個undefined的訊息

$ c++filt _ZTI1A
typeinfo for A

跟我們說A的typeinfo 從缺, 比起上面編譯器直接跟我們說少什麼, 這邊能有的資訊就更少了

尤其在大型專案中, 可能很難找到真正定義的位置在哪

會發生這個問題是我在使用pybind, 做c++與python的連結時, 發現so產生都好好的也沒有異狀, 但在import package時就會一直告訴我這個log, 花了一些時間才找到

在此紀錄一下

--

--

ss
ss

No responses yet