PY08-03:修饰名与动态库编译

2020-05-08  本文已影响0人  杨强AT南京

  考虑Python的性能,梳理下Cython的扩展,这个主题主要梳理下window平台下的动态库编译与使用技术之一:修饰名与动态库的关系。Linux下的动态库没有这么啰嗦,Windows下的修饰名在obj中就开始修饰,但是因为修饰名的缘故obj,lib,dll之间的修饰名配合工作一旦一个地方出错,会导致查找错误,就会产生那个鼎鼎大名的link错误:找不到外部引用符号。为了理解,我们使用def来导出函数,变量与类,而且使用VSCode来撸代码,很是酸爽。本主题涉及几个工具:cl, lib, link, dumpbin, undname等。


函数名,变量名与类型的内部表示

测试代码

头文件model.h

#ifndef YQ_MODEL_H
#define YQ_MODEL_H
int var_a = 20;

float calculate(float, float);

class Sobel{
public: 
    Sobel();
    int m_a;
    int getInfo(const char *);
private:
    void queryInfo(int);
};
#endif

实现文件model.cpp

#include "model.h"

float calculate(float p1, float p2){
    return p1 + p2;
}

Sobel::Sobel():m_a(88){

}

int Sobel::getInfo(const char *filename){
    return 77;
}

void Sobel::queryInfo(int id){

}

编译成目标文件

CL_ARGS    = /EHsc  \
             /MD \
             /source-charset:utf-8  \
             /execution-charset:utf-8 \
             /nologo

obj: model.cpp model.h
# 编译目标文件
    @cl /c $(CL_ARGS)  model.cpp


clean:
    @del *.obj *.dll *.pdb *.ilk *.exe *.lib  *.exp 2>/Nul


bumpbin工具查看修饰名

命令

修饰名输出

C:\01works\13python\codes\export_all>dumpbin /symbols  model.obj
Microsoft (R) COFF/PE Dumper Version 14.24.28319.0        
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file model.obj

File Type: COFF OBJECT

COFF SYMBOL TABLE
000 01056E9F ABS    notype       Static       | @comp.id
001 80000190 ABS    notype       Static       | @feat.00
002 00000000 SECT1  notype       Static       | .drectve
    Section length   2F, #relocs    0, #linenums    0, checksum        0
004 00000000 SECT2  notype       Static       | .debug$S
    Section length   80, #relocs    0, #linenums    0, checksum        0
006 00000000 SECT3  notype       Static       | .data
    Section length    4, #relocs    0, #linenums    0, checksum DF7BC0C8
008 00000000 SECT3  notype       External     | ?var_a@@3HA (int var_a)
009 00000000 SECT4  notype       Static       | .text$mn
    Section length   6A, #relocs    0, #linenums    0, checksum 4BDEC2ED
00B 00000000 SECT4  notype ()    External     | ?calculate@@YAMMM@Z (float __cdecl calculate(float,float))
00C 00000020 SECT4  notype ()    External     | ??0Sobel@@QEAA@XZ (public: __cdecl Sobel::Sobel(void))
00D 00000040 SECT4  notype ()    External     | ?getInfo@Sobel@@QEAAHPEBD@Z (public: int __cdecl Sobel::getInfo(char const *))
00E 00000060 SECT4  notype ()    External     | ?queryInfo@Sobel@@AEAAXH@Z (private: void __cdecl Sobel::queryInfo(int))
00F 00000000 UNDEF  notype       External     | _fltused
010 00000000 SECT5  notype       Static       | .chks64
    Section length   28, #relocs    0, #linenums    0, checksum        0

String Table Size = 0x6D bytes


C:\01works\13python\codes\export_all>nmake clean

Microsoft (R) 程序维护实用工具 14.24.28319.0 版
版权所有 (C) Microsoft Corporation。  保留所有权利。


C:\01works\13python\codes\export_all>nmake obj

C:\01works\13python\codes\export_all>dumpbin /symbols  model.obj
Microsoft (R) COFF/PE Dumper Version 14.24.28319.0        
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file model.obj

File Type: COFF OBJECT

COFF SYMBOL TABLE
000 01056E9F ABS    notype       Static       | @comp.id  
001 80000190 ABS    notype       Static       | @feat.00  
002 00000000 SECT1  notype       Static       | .drectve
    Section length   2F, #relocs    0, #linenums    0, checksum        0
004 00000000 SECT2  notype       Static       | .debug$S
    Section length   80, #relocs    0, #linenums    0, checksum        0
006 00000000 SECT3  notype       Static       | .data
    Section length    4, #relocs    0, #linenums    0, checksum DF7BC0C8
008 00000000 SECT3  notype       External     | ?var_a@@3HA (int var_a)
009 00000000 SECT4  notype       Static       | .text$mn
    Section length   6A, #relocs    0, #linenums    0, checksum 4BDEC2ED
00B 00000000 SECT4  notype ()    External     | ?calculate@@YAMMM@Z (float __cdecl calculate(float,float))
00C 00000020 SECT4  notype ()    External     | ??0Sobel@@QEAA@XZ (public: __cdecl Sobel::Sobel(void))
00D 00000040 SECT4  notype ()    External     | ?getInfo@Sobel@@QEAAHPEBD@Z (public: int __cdecl Sobel::getInfo(char const *))
00E 00000060 SECT4  notype ()    External     | ?queryInfo@Sobel@@AEAAXH@Z (private: void __cdecl Sobel::queryInfo(int))
00F 00000000 UNDEF  notype       External     | _fltused
010 00000000 SECT5  notype       Static       | .chks64
    Section length   28, #relocs    0, #linenums    0, checksum        0

String Table Size = 0x6D bytes

  Summary

          28 .chks64
           4 .data
          80 .debug$S
          2F .drectve
          6A .text$mn

C:\01works\13python\codes\export_all>

修饰名转换工具

C:\01works\13python\codes\export_all>undname ??0Sobel@@QEAA@XZ
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.

Undecoration of :- "??0Sobel@@QEAA@XZ"
is :- "public: __cdecl Sobel::Sobel(void) __ptr64"

修饰名与DLL符号调用

导出类,函数与变量

导出变量

头文件

#ifndef YQ_MODEL_H
#define YQ_MODEL_H
int var_a;
int var_b;

float calculate(float, float);

class Sobel{
public: 
    Sobel();
    int m_a;
    int getInfo(const char *);
private:
    void queryInfo(int);
};
#endif

实现文件

#include "model.h"

float calculate(float p1, float p2){
    var_a = 99;
    var_b = 199;
    return p1 + p2;
}

Sobel::Sobel():m_a(88){

}

int Sobel::getInfo(const char *filename){
    return 77;
}

void Sobel::queryInfo(int id){

}

def文件

LIBRARY libmodel
EXPORTS
    calculate            @1
    ?var_a@@3HA          @2 DATA
    var_b                @3 DATA

编译脚本

CL_ARGS    = /EHsc  \
             /MD \
             /source-charset:utf-8  \
             /execution-charset:utf-8 \
             /nologo

obj: model.cpp model.h
# 编译目标文件
    @cl /c $(CL_ARGS)  model.cpp
# 链接动态库
    @link /MACHINE:X64 /NOLOGO /DLL /DEF:model.def model.obj

main: main.cpp
    @cl $(CL_ARGS) main.cpp /link /OUT:main.exe  
clean:
    @del *.obj *.dll *.pdb *.ilk *.exe *.lib  *.exp 2>/Nul

查看lib的导出

C:\01works\13python\codes\export_all>dumpbin /exports model.lib
Microsoft (R) COFF/PE Dumper Version 14.24.28319.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file model.lib

File Type: LIBRARY

     Exports

       ordinal    name

             1    ?calculate@@YAMMM@Z (float __cdecl calculate(float,float))
             2    ?var_a@@3HA (int var_a)
             3    ?var_b@@3HA (int var_b)

  Summary

          C6 .debug$S
          14 .idata$2
          14 .idata$3
           8 .idata$4
           8 .idata$5
           E .idata$6

C:\01works\13python\codes\export_all>

查看dll的导出

C:\01works\13python\codes\export_all>dumpbin /exports libmodel.dll
Microsoft (R) COFF/PE Dumper Version 14.24.28319.0        
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file libmodel.dll

File Type: DLL

  Section contains the following exports for libmodel.dll 

    00000000 characteristics
    FFFFFFFF time date stamp
        0.00 version
           1 ordinal base
           3 number of functions
           3 number of names

    ordinal hint RVA      name

          2    0 00003040 ?var_a@@3HA
          1    1 00001000 calculate
          3    2 00003044 var_b

  Summary

        1000 .data
        1000 .pdata
        1000 .rdata
        1000 .reloc
        1000 .text

C:\01works\13python\codes\export_all>

调用导出的变量

#include <iostream>
#include <windows.h>

#pragma comment(lib, "model.lib")
__declspec(dllimport) int var_a;
__declspec(dllimport) int var_b;

float calculate(float, float);

int main(int argc, const char *argv){
    std::cout << "---------------编译调用-------------------" << std::endl;
    std::cout << var_a << std::endl;
    std::cout << var_b << std::endl;
    std::cout << calculate(45.0, 55.0) << std::endl;   
    std::cout << var_a << std::endl;
    std::cout << var_b << std::endl;

    std::cout << "---------------动态调用-------------------" << std::endl;
    HMODULE h = LoadLibraryA("libmodel.dll");
    if(h == NULL){
        std::cout << "加载动态库文件失败!" << std::endl;
        return -1;
    }
    else{
        std::cout << "加载动态库文件成功!" << std::endl;
    }
    // 查找符号函数
    // FARPROC v = GetProcAddress(h, "var_b");    // 注意def中的定义
    FARPROC v = GetProcAddress(h, "?var_a@@3HA");
    if(v == NULL){
        std::cout << "查找变量失败!" << std::endl;
        // 释放加载的库
        FreeLibrary(h);
        return -1;
    }
    else{
        std::cout << "查找变量成功!" << std::endl;
    }
    // 调用变量
    std::cout << *(int*)v << std::endl;

    FreeLibrary(h);
    
    return 0;
}

导出变量成功

导出类

#include "model.h"

float calculate(float p1, float p2){
    var_a = 99;
    var_b = 199;
    return p1 + p2;
}

Sobel::Sobel():m_a(88){

}

int Sobel::getInfo(const char *filename){
    return m_a;
}

void Sobel::queryInfo(int id){

}

确定修饰名


C:\01works\13python\codes\def_class>dumpbin /symbols model.obj
Microsoft (R) COFF/PE Dumper Version 14.24.28319.0        
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file model.obj

File Type: COFF OBJECT

COFF SYMBOL TABLE
000 01056E9F ABS    notype       Static       | @comp.id  
001 80000190 ABS    notype       Static       | @feat.00
002 00000000 SECT1  notype       Static       | .drectve
    Section length   2F, #relocs    0, #linenums    0, checksum        0
004 00000000 SECT2  notype       Static       | .debug$S
    Section length   80, #relocs    0, #linenums    0, checksum        0
006 00000000 SECT3  notype       Static       | .bss
    Section length    8, #relocs    0, #linenums    0, checksum        0
008 00000000 SECT3  notype       External     | ?var_a@@3HA (int var_a)
009 00000004 SECT3  notype       External     | ?var_b@@3HA (int var_b)
00A 00000000 SECT4  notype       Static       | .text$mn
    Section length   8A, #relocs    2, #linenums    0, checksum 3B5C665F
00C 00000000 SECT4  notype ()    External     | ?calculate@@YAMMM@Z (float __cdecl calculate(float,float))
00D 00000040 SECT4  notype ()    External     | ??0Sobel@@QEAA@XZ (public: __cdecl Sobel::Sobel(void))
00E 00000060 SECT4  notype ()    External     | ?getInfo@Sobel@@QEAAHPEBD@Z (public: int __cdecl Sobel::getInfo(char const *))
00F 00000080 SECT4  notype ()    External     | ?queryInfo@Sobel@@AEAAXH@Z (private: void __cdecl Sobel::queryInfo(int))
010 00000000 UNDEF  notype       External     | _fltused
011 00000000 SECT5  notype       Static       | .chks64
    Section length   28, #relocs    0, #linenums    0, checksum        0

String Table Size = 0x79 bytes

  Summary

           8 .bss
          28 .chks64
          80 .debug$S
          2F .drectve
          8A .text$mn
00D 00000040 SECT4  notype ()    External     | ??0Sobel@@QEAA@XZ (public: __cdecl Sobel::Sobel(void))
00E 00000060 SECT4  notype ()    External     | ?getInfo@Sobel@@QEAAHPEBD@Z (public: int __cdecl Sobel::getInfo(char const *))
00F 00000080 SECT4  notype ()    External     | ?queryInfo@Sobel@@AEAAXH@Z (private: void __cdecl Sobel::queryInfo(int))

编写def文件:model.def

LIBRARY libmodel
EXPORTS
    ??0Sobel@@QEAA@XZ              @1
    ?getInfo@Sobel@@QEAAHPEBD@Z    @2
    ?queryInfo@Sobel@@AEAAXH@Z     @3

编译dll

CL_ARGS    = /EHsc  \
             /MD \
             /source-charset:utf-8  \
             /execution-charset:utf-8 \
             /nologo

obj: model.cpp model.h
# 编译目标文件
    @cl /c $(CL_ARGS)  model.cpp
# 链接动态库
    @link /MACHINE:X64 /NOLOGO /DLL /DEF:model.def model.obj

main: main.cpp
    @cl $(CL_ARGS) main.cpp /link /OUT:main.exe  
clean:
    @del *.obj *.dll *.pdb *.ilk *.exe *.lib  *.exp 2>/Nul


查看model.lib的导出符号

C:\01works\13python\codes\def_class>dumpbin /exports model.lib
Microsoft (R) COFF/PE Dumper Version 14.24.28319.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file model.lib

File Type: LIBRARY

     Exports

       ordinal    name

             1    ??0Sobel@@QEAA@XZ (public: __cdecl Sobel::Sobel(void))
             2    ?getInfo@Sobel@@QEAAHPEBD@Z (public: int __cdecl Sobel::getInfo(char const *))
             3    ?queryInfo@Sobel@@AEAAXH@Z (private: void __cdecl Sobel::queryInfo(int))

  Summary

          C6 .debug$S
          14 .idata$2
          14 .idata$3
           8 .idata$4
           8 .idata$5
           E .idata$6

C:\01works\13python\codes\def_class>

查看model.dll导出的符号

C:\01works\13python\codes\def_class>dumpbin /exports libmodel.dll
Microsoft (R) COFF/PE Dumper Version 14.24.28319.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file libmodel.dll

File Type: DLL

  Section contains the following exports for libmodel.dll

    00000000 characteristics
    FFFFFFFF time date stamp
        0.00 version
           1 ordinal base
           3 number of functions
           3 number of names

    ordinal hint RVA      name

          1    0 00001040 ??0Sobel@@QEAA@XZ
          2    1 00001060 ?getInfo@Sobel@@QEAAHPEBD@Z
          3    2 00001080 ?queryInfo@Sobel@@AEAAXH@Z

  Summary

        1000 .data
        1000 .pdata
        1000 .rdata
        1000 .reloc
        1000 .text

C:\01works\13python\codes\def_class>

调用导出的类

#include <iostream>
#include <windows.h>
#include "model.h"

#pragma comment(lib, "model.lib")

typedef Sobel (*builder)();

int main(int argc, const char *argv){
    std::cout << "---------------编译调用-------------------" << std::endl;
    Sobel s;   // 构造对象
    int re = s.getInfo("Hello"); // 调用成员
    std::cout << "动态库调用结果:" << re << std::endl; 
    
    std::cout << "---------------编译调用-------------------" << std::endl;
    HMODULE h = LoadLibraryA("libmodel.dll");
    if(h == NULL){
        std::cout << "加载动态库文件失败!" << std::endl;
        return -1;
    }
    else{
        std::cout << "加载动态库文件成功!" << std::endl;
    }
    // 查找符号函数
    FARPROC cls_name = GetProcAddress(h, MAKEINTRESOURCEA(1)); 
    FARPROC cls_getInfo = GetProcAddress(h, MAKEINTRESOURCEA(2));    
    if(cls_name == NULL || cls_getInfo == NULL){
        std::cout << "查找类或者成员函数失败!" << std::endl;
        // 释放加载的库
        FreeLibrary(h);
        return -1;
    }
    else{
        std::cout << "查找类构造器与成员函数成功!" << std::endl;
    }
    // 调用变量
    builder con = (builder)cls_name;
    Sobel sobel = con();
    int r = sobel.getInfo("Hello"); // 调用成员
    std::cout << "动态库手工调用结果:" << r << std::endl; 
    std::cout << "---------------------------------------" << std::endl;
    int (*cal)(const char *) =(int (*)(const char *)) cls_getInfo;     // 没有对象依赖,指向没有初始化的全局栈位置。
    std::cout << "动态库手工调用结果:" << cal("world") << std::endl;
    FreeLibrary(h);
    
    return 0;
}


附录


上一篇 下一篇

猜你喜欢

热点阅读