CALL_CONVENTION

MSVC

为了让两个不同调用约定的能够互相调用,由于不同调用约定对于符号的不同修饰,我选择使用 dll 手动修改导入符号,防止符号错乱的问题。

这篇文章介绍了 MSVC 下各种调用约定。同一种调用约定在不同指令集架构下也是有区别的。
https://docs.microsoft.com/en-us/cpp/build/reference/gd-gr-gv-gz-calling-convention?view=msvc-160
其中有一个 vectorcall 似乎是为了优化而使用的。

0x01

//CdeclCall.c
#include <stdio.h>

//__declspec(dllexport) 
void  __cdecl foo(int a,int b){
    printf("a = %d , b = %d ",a,b);
}
//FastCall.c
__declspec(dllimport) void __fastcall foo(int,int);

int main(){
    foo(1,2);
    return 0;
}

我们先看看 FastCall.c 编译出来的符号是什么形式的。

cl /c CdeclCall.c
dumpbin /SYMBOLS CdeclCall.obj

以下是结果

Microsoft (R) COFF/PE Dumper Version 14.28.29913.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file CdeclCall.obj

File Type: COFF OBJECT

COFF SYMBOL TABLE
000 010474D9 ABS    notype       Static       | @comp.id
001 80010191 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   88, #relocs    0, #linenums    0, checksum        0
006 00000000 SECT3  notype       Static       | .text$mn
    Section length   1A, #relocs    2, #linenums    0, checksum 7C27059A
008 00000000 SECT4  notype       Static       | .text$mn
    Section length    A, #relocs    1, #linenums    0, checksum 71A05264, selection    2 (pick any)
00A 00000000 SECT5  notype       Static       | .text$mn
    Section length   29, #relocs    2, #linenums    0, checksum 2B25B17F, selection    2 (pick any)
00C 00000000 SECT6  notype       Static       | .text$mn
    Section length   3A, #relocs    2, #linenums    0, checksum CAE6D625, selection    2 (pick any)
00E 00000000 SECT4  notype ()    External     | ___local_stdio_printf_options
00F 00000000 UNDEF  notype ()    External     | ___acrt_iob_func
010 00000000 UNDEF  notype ()    External     | ___stdio_common_vfprintf
011 00000000 SECT5  notype ()    External     | __vfprintf_l
012 00000000 SECT6  notype ()    External     | _printf
013 00000000 SECT3  notype ()    External     | _foo
014 00000008 UNDEF  notype       External     | ?_OptionsStorage@?1??__local_stdio_printf_options@@9@9 (`__local_stdio_printf_options'::`2'::_OptionsStorage)
015 00000000 SECT7  notype       Static       | .data
    Section length   11, #relocs    0, #linenums    0, checksum A358B0F1
017 00000000 SECT7  notype       Static       | $SG9219
018 00000000 SECT8  notype       Static       | .chks64
    Section length   40, #relocs    0, #linenums    0, checksum        0

String Table Size = 0x90 bytes

  Summary

          40 .chks64
          11 .data
          88 .debug$S
          2F .drectve
          87 .text$mn

可以看见在 MSVC cdecl 下 foo 只是被简单修饰成为了 _foo,

但是当我们用同样的方法去查看 fastcall 修饰的 foo 时,会发现出现了一些变化。

@foo@8

很明显这两个不能链接。由此要有一些手动的操作,配置符号。

LIBRARY CdeclCall
EXPORTS
    foo
cl .\CdeclCall.c /LD /DEF .\together.def

最后

link .\FastCall.obj .\CdeclCall.lib

得到一个神魔玩意儿

PS D:\Learn_Linker_elf_rtld\call_invention> D:\Learn_Linker_elf_rtld\call_invention\FastCall.exe
PS D:\Learn_Linker_elf_rtld\call_invention>

执行不了,ida 看看,程序上好像没什么问题,可能原因在动态链接器?留坑。。。

0x10

还有一种显示的动态链接,还没试过,可以尝试一波。

去官网找了一个 显示动态链接的模板,修改后有以下结果。

#include <windows.h> 
#include <stdio.h> 
 
//typedef int (__cdecl *MYPROC)(LPWSTR); 

typedef void (__fastcall *MYPROC)(int,int);

int main( void ) 
{ 
    HINSTANCE hinstLib; 
    MYPROC foo; 
    BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; 
 
    // Get a handle to the DLL module.
 
    hinstLib = LoadLibrary(TEXT("CdeclCall.dll")); 
 
    // If the handle is valid, try to get the function address.
 
    if (hinstLib != NULL) 
    { 
        foo = (MYPROC) GetProcAddress(hinstLib, "foo"); 
 
        // If the function address is valid, call the function.
 
        if (NULL != foo) 
        {
            fRunTimeLinkSuccess = TRUE;
            foo(1,2);
        }
        // Free the DLL module.
 
        fFreeResult = FreeLibrary(hinstLib); 
    } 

    // If unable to call the DLL function, use an alternative.
    if (! fRunTimeLinkSuccess) 
        printf("Message printed from executable\n"); 

    return 0;

}

用 together.def CdeclCall.c 编译出 CdeclCall.dll

运行
有如下结果

PS D:\Learn_Linker_elf_rtld\call_invention> .\D_FastCall.exe
a = 608420 , b = 1

预期之中。好像学到了些什么。2333

GCC

留坑。。。

ld 的脚本麻烦一点,不过资料应该多一点。