C_and_CPP 板


LINE

我覺得這個直接拿個例子來解釋如何在執行時載入 C++ shared library, 並建構和操作 DSO 內定義的 class 和 function: ============================================================================== // foo.hpp struct foo { virtual ~foo() = 0; virtual auto vfn() const -> int = 0; auto fn() const -> int; }; inline foo::~foo() = default; template<typename... Ts> using make_foo_fn = auto (*)(Ts...) -> foo*; ============================================================================== // foo.cpp #include "foo.hpp" struct foo_impl : foo { virtual ~foo_impl() = default; virtual auto vfn() const -> int override { return fn(); } }; extern "C" { auto new_foo() -> foo* { return new foo_impl; } } ============================================================================== // main.cpp #include <cassert> #include <iostream> #include <memory> #include <dlfcn.h> #include "foo.hpp" // C++17 scopeguard utility #include <type_traits> template<typename F> struct scopeguard : F { scopeguard(scopeguard&&) = delete; ~scopeguard() { (*this)(); } }; template<typename F> scopeguard(F) -> scopeguard<std::decay_t<F>>; #define PP_CAT_IMPL(x, y) x##y #define PP_CAT(x, y) PP_CAT_IMPL(x, y) #define at_scope_exit \ [[maybe_unused]] const auto PP_CAT(sg$, __COUNTER__) = scopeguard auto foo::fn() const -> int { return 42; } int main() try { auto libfoo = dlopen("./libfoo.so", RTLD_NOW | RTLD_GLOBAL); assert(libfoo); at_scope_exit { [&] { dlclose(libfoo); } }; const auto new_foo = reinterpret_cast<make_foo_fn<>>(dlsym(libfoo, "new_foo")); assert(new_foo); const auto f = std::unique_ptr<foo>{new_foo()}; std::cout << f->vfn() << std::endl; } catch (...) { throw; } ============================================================================== $ GXX='g++ -std=c++17 -Wall -Wextra -pedantic -O2' $ ${GXX} -fPIC -shared foo.cpp -o libfoo.so $ ${GXX} -rdynamic main.cpp -ldl $ ./a.out 42 線上版:http://coliru.stacked-crooked.com/a/0f8900f100523fc7 這裡在 foo.hpp 裡面宣告 foo 為一個 abstract class 並定義 foo 的 interface, main() 可以透過這個 interface 操作 foo, 而 foo 的實做 foo_impl 定義在 foo.cpp 裡面, 並編譯成 libfoo.so 讓 main() 透過 dlopen("path/to/libfoo.so", ...) 載入, 但這會有一個問題,main() 根本就不知道 foo_impl 的存在, 所以 foo.cpp 會定義一個 make_foo 的 function 回傳一個 foo*, 而 main() 可以用 dlsym(..., "make_foo"); 拿到 foo*, 並透過呼叫 foo 的 virtual function 就可以 dispatch 到 foo_impl 的實做 這裡更有趣的就是: foo_impl::vfn() 會呼叫 foo::fn() 這個不是 virtual 的 function, 而且他的定義在 main.cpp 裡面 (當然也有可能是別的 translation unit), 所以 libfoo.so 是看不到 foo:fn() 的定義的, 而 link 的時候要加上 -rdynamic (也就是 ld 的 --export-dynamic), 讓 linker 將 foo::fn() 加到 dynamic symbol table 裡面, 當 main() 用 dlopen() 呼叫 dynamic loader 將 libfoo.so 載入時, 在處理 run-time relocation 時便可以找到 foo::fn() 實際上去看 gcc 的實做,基本上是遵照 Itanium C++ ABI foo 的 layout 是這樣的: foo vtable for foo (_ZTV8foo_impl) 0x0 [vptr] - 0x0 [ offset to top ] | 0x8 [ pointer to typeinfo ] --------> 0x10 [ ptr to ~foo() D1 ] 0x18 [ ptr to ~foo() D0 ] 0x20 [ ptr to vfn() ] 而 libfoo.so 裡面的 foo_impl 的 vtbl: $ nm libfoo.so | grep _ZTV8foo_impl 0000000000200dc0 V _ZTV8foo_impl $ readelf -r libfoo.so Offset Info Type Sym. Value Sym. Name + Addend 000000200dd0 000c00000001 R_X86_64_64 0000000000000a60 _ZN8foo_implD1Ev + 0 000000200dd8 000e00000001 R_X86_64_64 0000000000000a80 _ZN8foo_implD0Ev + 0 000000200de0 001800000001 R_X86_64_64 0000000000000a70 _ZNK8foo_impl3vfnEv + 0 當 dlopen 載入 libfoo.so 時, 會將 foo_impl 的 vtbl + 0x20 寫入 foo_impl::vfn() 的位置, 所以當執行到 main() 的 foo* f = make_foo(); 後,f 的 layout 就是: (假設 f 在 0x100000,_ZTV8foo_impl 被 load 到 0x200dc0) f @ 0x100000 vtable for foo_impl @ 0x200dc0 0x100000 [ 0x200dd0 ] - 0x200dc0 [ 0 ] | 0x200dc8 [ _ZTI8foo_impl ] --------> 0x200dd0 [ foo::~foo() D1 ] 0x200dd8 [ foo::~foo() D0 ] 0x200de0 [ foo_impl::vfn() ] 而 main 中呼叫 f->vfn() 的 code 則是這樣 (f = 0x100000 在 rax 裡面): movq %rax, %rbx # rbx = 0x100000 movq (%rax), %rax # rax = *0x100000 = 0x200dd0 movq %rbx, %rdi # rdi = 0x100000 (this) call *16(%rax) # call *(0x200de0) 這樣就會呼叫到 foo_impl::vfn() 而 foo_impl::vfn() 的實做需要呼叫 foo::fn(), 這部份就和一般透過 PLT 間接呼叫動態連結的 function 原理是一樣的: 0000000000000a70 <_ZNK8foo_impl3vfnEv>: a70: e9 ab fe ff ff jmpq 920 <_ZNK3foo2fnEv@plt> foo::fn() 的 PLT entry: 0000000000000920 <_ZNK3foo2fnEv@plt>: 920: ff 25 f2 06 20 00 jmpq *0x2006f2(%rip) # 201018 <_ZNK3foo2fnEv> 926: 68 00 00 00 00 pushq $0x0 92b: e9 e0 ff ff ff jmpq 910 <.plt> $ readelf -r libfoo.so | grep _ZNK3foo2fnEv 000000201018 000100000007 R_X86_64_JUMP_SLOT 0000000000000000 _ZNK3foo2fnEv + 0 因為這裡有給 RTLD_NOW,(若是 RTLD_LAZY 會是第一次呼叫時才會做 binding) 所以 dlopen libfoo.so 時,便會找到執行時 foo::fn() 的位置, 並將 jump slot 改寫成直接 jump 至 foo::fn(): 0x600000 <foo::fn()@plt>: 0x200000 <foo::fn()> jmp foo::fn() ------------------> movl $42, %eax ret 這也是為什麼 main.cpp 必須將 foo::fn() export 到 dynamic symbol table 中 這裡細節真的很多,基本上你把 C++ ABI 和 dynamic linking 的運作模式弄懂, 就知道如何在執行時動態載入 C++ 的 DSO --



※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 140.113.193.217
※ 文章網址: https://webptt.com/m.aspx?n=bbs/C_and_CPP/M.1507385491.A.794.html
1F:→ xam: 我覺得看得懂這篇的話, 沒道理google找的看不懂.. 10/07 23:00
2F:推 Bencrie: 推詳解 XD 10/07 23:10
3F:推 stucode: 推,這要解釋清楚真的需要一點篇幅。 10/07 23:17
4F:推 mabinogi805: 也太詳細QQ 10/08 00:03
※ 編輯: PkmX (140.113.193.217), 10/08/2017 01:08:17
5F:→ dreamboat66: 謝謝詳細的解說,努力理解中 10/08 02:37







like.gif 您可能會有興趣的文章
icon.png[問題/行為] 貓晚上進房間會不會有憋尿問題
icon.pngRe: [閒聊] 選了錯誤的女孩成為魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一張
icon.png[心得] EMS高領長版毛衣.墨小樓MC1002
icon.png[分享] 丹龍隔熱紙GE55+33+22
icon.png[問題] 清洗洗衣機
icon.png[尋物] 窗台下的空間
icon.png[閒聊] 双極の女神1 木魔爵
icon.png[售車] 新竹 1997 march 1297cc 白色 四門
icon.png[討論] 能從照片感受到攝影者心情嗎
icon.png[狂賀] 賀賀賀賀 賀!島村卯月!總選舉NO.1
icon.png[難過] 羨慕白皮膚的女生
icon.png閱讀文章
icon.png[黑特]
icon.png[問題] SBK S1安裝於安全帽位置
icon.png[分享] 舊woo100絕版開箱!!
icon.pngRe: [無言] 關於小包衛生紙
icon.png[開箱] E5-2683V3 RX480Strix 快睿C1 簡單測試
icon.png[心得] 蒼の海賊龍 地獄 執行者16PT
icon.png[售車] 1999年Virage iO 1.8EXi
icon.png[心得] 挑戰33 LV10 獅子座pt solo
icon.png[閒聊] 手把手教你不被桶之新手主購教學
icon.png[分享] Civic Type R 量產版官方照無預警流出
icon.png[售車] Golf 4 2.0 銀色 自排
icon.png[出售] Graco提籃汽座(有底座)2000元誠可議
icon.png[問題] 請問補牙材質掉了還能再補嗎?(台中半年內
icon.png[問題] 44th 單曲 生寫竟然都給重複的啊啊!
icon.png[心得] 華南紅卡/icash 核卡
icon.png[問題] 拔牙矯正這樣正常嗎
icon.png[贈送] 老莫高業 初業 102年版
icon.png[情報] 三大行動支付 本季掀戰火
icon.png[寶寶] 博客來Amos水蠟筆5/1特價五折
icon.pngRe: [心得] 新鮮人一些面試分享
icon.png[心得] 蒼の海賊龍 地獄 麒麟25PT
icon.pngRe: [閒聊] (君の名は。雷慎入) 君名二創漫畫翻譯
icon.pngRe: [閒聊] OGN中場影片:失蹤人口局 (英文字幕)
icon.png[問題] 台灣大哥大4G訊號差
icon.png[出售] [全國]全新千尋侘草LED燈, 水草

請輸入看板名稱,例如:Soft_Job站內搜尋

TOP