如何在没有头文件的情况下调用动态库的类的私有成员函数

如何在没有头文件的情况下调用动态库的类成员函数


源代码 https://github.com/TonyBeen/study/tree/master/dlopen

编写一个不存在虚函数的类
// class.h
#pragma once

class CClass
{
public:
    CClass();
    ~CClass();

private:
    void show();

private:
    char _buffer[64];
};

// class.cc
#include "class.h"
#include <stdio.h>
#include <string.h>

CClass::CClass()
{
    size_t size = sizeof(_buffer);
    memset(_buffer, '1', size);
    _buffer[size - 1] = '\0';
}

CClass::~CClass()
{
}

void CClass::show()
{
    printf("%s\n", _buffer);
}

extern "C" {
    CClass* createClass() {
        return new CClass();
    }

    void destroyClass(CClass* obj) {
        delete obj;
    }
}

编译指令: g++ -shared -fPIC -o libmyclass.so class.cc -O2

测试代码
// test_class.cc
#include <iostream>
#include <dlfcn.h>

typedef void* (*CreateFunction)();
typedef void (*DestroyFunction)(void *);

typedef void (*DoShow)(void *);

int main(int argc, char **argv)
{
    void* handle = dlopen("./libmyclass.so", RTLD_LAZY);
    if (!handle) {
        std::cerr << "Error opening shared library: " << dlerror() << std::endl;
        return 1;
    }

    // 获取创建和销毁函数的指针
    CreateFunction createFunc = (CreateFunction)dlsym(handle, "createClass");
    if (!createFunc) {
        std::cerr << "Error loading create function: " << dlerror() << std::endl;
        dlclose(handle);
        return 1;
    }

    DestroyFunction destroyFunc = (DestroyFunction)dlsym(handle, "destroyClass");
    if (!destroyFunc) {
        std::cerr << "Error loading destroy function: " << dlerror() << std::endl;
        dlclose(handle);
        return 1;
    }

    void* obj = createFunc();
    if (!obj) {
        std::cerr << "Error creating MyClass instance" << std::endl;
        dlclose(handle);
        return 1;
    }

    DoShow pFnShow = (DoShow)dlsym(handle, "_ZN6CClass4showEv");
    pFnShow(obj);

    destroyFunc(obj);

    // 关闭共享库
    dlclose(handle);

    return 0;
}

编译指令: g++ test_class.cc -o test_class -O2 -ldl

输出内容: 111111111111111111111111111111111111111111111111111111111111111

_ZN6CClass4showEv如何获取

获取方式有三种:
1、nm libmyclass.so

0000000000004058 b completed.7658
00000000000011c0 T createClass
w __cxa_finalize@@GLIBC_2.2.5
00000000000010a0 t deregister_tm_clones
0000000000001200 T destroyClass
0000000000001130 t __do_global_dtors_aux
0000000000003df8 d __do_global_dtors_aux_fini_array_entry
0000000000004048 d __dso_handle
0000000000004050 d DW.ref.__gxx_personality_v0
0000000000003e00 d _DYNAMIC
0000000000001224 t _fini
0000000000001170 t frame_dummy
0000000000003df0 d __frame_dummy_init_array_entry
0000000000002158 r FRAME_END
0000000000004000 d GLOBAL_OFFSET_TABLE
w gmon_start
0000000000002010 r __GNU_EH_FRAME_HDR
U __gxx_personality_v0@@CXXABI_1.3
0000000000001000 t _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U puts@@GLIBC_2.2.5
00000000000010e0 t register_tm_clones
0000000000004058 d TMC_END
U _Unwind_Resume@@GCC_3.0
U _ZdlPvm@@CXXABI_1.3.9
00000000000011b0 T _ZN6CClass4showEv
0000000000001180 T _ZN6CClassC1Ev
0000000000001180 T _ZN6CClassC2Ev
00000000000011a0 T _ZN6CClassD1Ev
00000000000011a0 T _ZN6CClassD2Ev
U _Znwm@@GLIBCXX_3.4

2、readelf -s libmyclass.so

Symbol table ‘.dynsym’ contains 17 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _Znwm@GLIBCXX_3.4 (3)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZdlPvm@CXXABI_1.3.9 (4)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __gxx_personality_v0@CXXABI_1.3 (5)
6: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _Unwind_Resume@GCC_3.0 (6)
8: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start
9: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
10: 0000000000001200 34 FUNC GLOBAL DEFAULT 12 destroyClass
11: 00000000000011c0 61 FUNC GLOBAL DEFAULT 12 createClass
12: 0000000000001180 28 FUNC GLOBAL DEFAULT 12 _ZN6CClassC2Ev
13: 00000000000011a0 2 FUNC GLOBAL DEFAULT 12 _ZN6CClassD1Ev
14: 00000000000011a0 2 FUNC GLOBAL DEFAULT 12 _ZN6CClassD2Ev
15: 0000000000001180 28 FUNC GLOBAL DEFAULT 12 _ZN6CClassC1Ev
16: 00000000000011b0 5 FUNC GLOBAL DEFAULT 12 _ZN6CClass4showEv

Symbol table ‘.symtab’ contains 62 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000238 0 SECTION LOCAL DEFAULT 1
2: 0000000000000260 0 SECTION LOCAL DEFAULT 2
3: 00000000000002a0 0 SECTION LOCAL DEFAULT 3
4: 0000000000000438 0 SECTION LOCAL DEFAULT 4
5: 000000000000058a 0 SECTION LOCAL DEFAULT 5
6: 00000000000005b0 0 SECTION LOCAL DEFAULT 6
7: 0000000000000630 0 SECTION LOCAL DEFAULT 7
8: 00000000000006f0 0 SECTION LOCAL DEFAULT 8
9: 0000000000001000 0 SECTION LOCAL DEFAULT 9
10: 0000000000001020 0 SECTION LOCAL DEFAULT 10
11: 0000000000001090 0 SECTION LOCAL DEFAULT 11
12: 00000000000010a0 0 SECTION LOCAL DEFAULT 12
13: 0000000000001224 0 SECTION LOCAL DEFAULT 13
14: 0000000000002000 0 SECTION LOCAL DEFAULT 14
15: 0000000000002010 0 SECTION LOCAL DEFAULT 15
16: 0000000000002058 0 SECTION LOCAL DEFAULT 16
17: 000000000000215c 0 SECTION LOCAL DEFAULT 17
18: 0000000000003df0 0 SECTION LOCAL DEFAULT 18
19: 0000000000003df8 0 SECTION LOCAL DEFAULT 19
20: 0000000000003e00 0 SECTION LOCAL DEFAULT 20
21: 0000000000003fe0 0 SECTION LOCAL DEFAULT 21
22: 0000000000004000 0 SECTION LOCAL DEFAULT 22
23: 0000000000004048 0 SECTION LOCAL DEFAULT 23
24: 0000000000004058 0 SECTION LOCAL DEFAULT 24
25: 0000000000000000 0 SECTION LOCAL DEFAULT 25
26: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
27: 00000000000010a0 0 FUNC LOCAL DEFAULT 12 deregister_tm_clones
28: 00000000000010e0 0 FUNC LOCAL DEFAULT 12 register_tm_clones
29: 0000000000001130 0 FUNC LOCAL DEFAULT 12 __do_global_dtors_aux
30: 0000000000004058 1 OBJECT LOCAL DEFAULT 24 completed.7658
31: 0000000000003df8 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fin
32: 0000000000001170 0 FUNC LOCAL DEFAULT 12 frame_dummy
33: 0000000000003df0 0 OBJECT LOCAL DEFAULT 18 _frame_dummy_init_array
34: 0000000000000000 0 FILE LOCAL DEFAULT ABS class.cc
35: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
36: 0000000000002158 0 OBJECT LOCAL DEFAULT 16 FRAME_END
37: 0000000000000000 0 FILE LOCAL DEFAULT ABS
38: 0000000000002010 0 NOTYPE LOCAL DEFAULT 15 __GNU_EH_FRAME_HDR
39: 0000000000004048 0 OBJECT LOCAL DEFAULT 23 __dso_handle
40: 0000000000004050 8 OBJECT LOCAL DEFAULT 23 DW.ref._gxx_personality
41: 0000000000001224 0 FUNC LOCAL DEFAULT 13 _fini
42: 0000000000001000 0 FUNC LOCAL DEFAULT 9 _init
43: 0000000000003e00 0 OBJECT LOCAL DEFAULT 20 _DYNAMIC
44: 0000000000004058 0 OBJECT LOCAL DEFAULT 23 TMC_END
45: 0000000000004000 0 OBJECT LOCAL DEFAULT 22 GLOBAL_OFFSET_TABLE
46: 00000000000011c0 61 FUNC GLOBAL DEFAULT 12 createClass
47: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@@GLIBC_2.2
48: 0000000000001180 28 FUNC GLOBAL DEFAULT 12 _ZN6CClassC2Ev
49: 00000000000011a0 2 FUNC GLOBAL DEFAULT 12 _ZN6CClassD1Ev
50: 00000000000011b0 5 FUNC GLOBAL DEFAULT 12 _ZN6CClass4showEv
51: 00000000000011a0 2 FUNC GLOBAL DEFAULT 12 _ZN6CClassD2Ev
52: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _Znwm@@GLIBCXX_3.4
53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZdlPvm@@CXXABI_1.3.9
54: 0000000000001180 28 FUNC GLOBAL DEFAULT 12 _ZN6CClassC1Ev
55: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
56: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __gxx_personality_v0@@CXX
57: 0000000000001200 34 FUNC GLOBAL DEFAULT 12 destroyClass
58: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
59: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _Unwind_Resume@@GCC_3.0
60: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start
61: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable

3、objdump -x libmyclass.so
此处输出过多, 不在展示

通过以上方式均可获取到符号表
只需要找到_ZN6CClass4showEv即可

调用

普通函数的调用方式为直接调用,如createClass。
类的成员函数调用:

class T
{
public:
	void print();
};

T t = new T();
auto pFnPrint = &T::print;
(t->*pFnPrint)();

然这么写在编译时确实没问题,但是动态加载的情况下又没有类的头文件,则无法这么写,因为无法确定函数的地址。
所以需要使用dlsym获取到地址,但是g++在编译时会对函数名称进行修改,如上所示。故传递时需要传修改后的名字。
另一方面,类的成员函数即使在书写时无参,那么编译后其实也存在参数的,即类的this。
所以在生命函数时将其声明为typedef void (*DoShow)(void *);调用也就和普通函数一致。

注意:以上不涉及虚函数时可正常使用,当存在虚函数时,会存在虚函数表,无法确定被调用函数真正的地址。

  • 11
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值