Visual c++ 使用cl.exe将asm文件编译为dll

Visual c++ 使用cl.exe将asm文件编译为dll,visual-c++,assembly,masm,Visual C++,Assembly,Masm,给定:带有入口点的程序cprog 我通常这样做 cl.exe /MD /LD /Fe"prog.dll" /Fo"prog" "prog.c" /link ext.lib 或 在这两种情况下,生成的prog.dll都可以正常工作 现在,我执行以下操作以获取asm文件而不是obj文件: cl.exe /c /MD /Fa"prog" 到目前为止,这也“有效”。但是我不知道如何制作这个文件的dll 尝试: ml.exe /c /Cx /coff prog.asm cl.exe /MD /LD /

给定:带有入口点的程序c
prog

我通常这样做

cl.exe /MD /LD /Fe"prog.dll" /Fo"prog" "prog.c" /link ext.lib

在这两种情况下,生成的prog.dll都可以正常工作

现在,我执行以下操作以获取asm文件而不是obj文件:

cl.exe /c /MD /Fa"prog"
到目前为止,这也“有效”。但是我不知道如何制作这个文件的dll

尝试:

ml.exe /c /Cx /coff prog.asm
cl.exe /MD /LD /Fe"prog.dll" "prog.obj" /link ext.lib
结果:prog.dll没有入口点
prog

再次尝试:

ml.exe /c /Cx /coff prog.asm
cl.exe /MD /LD /Fe"prog.dll" "prog.obj" /link /entry:prog ext.lib
结果:编译器警告错误的入口点_prog不是具有12字节参数的stdcall,以及编译器关于未解析符号_memcpy的错误

问题:有没有办法将cl.exe通过/Fa生成的asm文件编译成dll(如果不可能使用ml.exe,最好通过cl.exe)

有没有办法将cl.exe通过
/Fa
生成的asm文件编译成dll(如果不可能使用ml.exe,最好通过cl.exe)

否:

  • C/C++编译器(cl.exe)无法汇编程序集代码输入。它只需要C或C++源代码作为输入。汇编程序是MASM(ml.exe)
  • 通常,cl.exe的汇编代码输出不能直接输入MASM。在某些情况下,它甚至不是有效的汇编代码。在其他情况下,MASM不直接支持代码中发出的指令、关键字和其他内容。如果C/C++源代码使用异常,事情会变得特别棘手。列表文件仅供参考
  • 我不清楚你为什么一开始就想这么做。如果源代码是C或C++,可以用MSVC编译和链接,那么引入附加中间步骤将其转换成汇编语言的要点是什么?直接使用cl.exe创建一个DLL


    如果您绝对必须这样做,那么您必须在通过MASM运行它之前获取MSVC生成的ASM列表文件并手动清理它。您可以通过关闭整个程序优化、关闭异常处理、关闭安全检查/cookie以及向链接器指示映像不包含安全的SEH处理程序来简化此清理任务。请注意,其中一些可能会破坏或更改代码的行为!您还需要为从运行库调用的函数添加
    EXTERN
    定义。

    虽然Microsoft C编译器生成的ASM源代码可能不是返回MASM的最佳输入,但这并不意味着它不起作用,至少在某些情况下是如此(只是可能不太复杂)。如果你看一看C编译器生成的ASM文件,你会发现微软的某个人费了很大的劲插入了各种“hacky”包括、指令、手动段定义和其他MASM细节,使源文件至少有很小的机会被反馈到MASM中并获得汇编结果。只要您将期望值设置得较低,我想,如果您的命令行选项正确,那么一个简单的C源文件,转换为ASM,然后反馈到MASM应该可以工作

    您需要记住的一个警告是,如果您像现在这样使用CRT(即使用memcmp),您将希望允许默认入口点___DllMainCRTStartup@12从相应的CRT.LIB文件中选择,而不是指定自己的文件。这允许在调用DllMain之前初始化CRT,从而防止在调用依赖于此初始化的某些CRT函数时发生崩溃。话虽如此,VisualStudio的旧版本,如7.1(2003),您可以不初始化CRT,这取决于您使用的函数,而不会有崩溃的风险。如果进程以前没有调用maincrtstartup或DllMainCRTStartup,则无论调用哪个CRT函数,较新版本的C运行时都会引发异常

    出于教育目的,让我们使用MSVC 7.1(2003)解决您上面描述的入口点问题,我们不必担心初始化CRT,以便您可以显式指定自己的入口点。我认为您正在点击以下链接器警告:

    warning LNK4086: entrypoint '_prog@XX' is not __stdcall with 12 bytes of arguments; image may not run
    
    当指定自己的DLL入口点时,链接器需要一个DllMain签名(它是12个参数字节和stdcall,因此函数会清除参数本身);官方的说法是:

    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
    
    您可以实现entrypoint函数,如本版本的prog.c所示:

    #include <Windows.h>
    #include <stdio.h>
    
    #pragma warning (disable:4100) //Warning Level 4: unreferenced formal parameter
    
    int __stdcall prog(DWORD hInst, DWORD dwReason, DWORD dwReserved)
    {
        printf("Result of memcmp: %d\n",memcmp("foo","bar",3));
        return(1);
    }
    
    您已经知道可以使用C编译器构建原始C源代码。 专注于从/Fa选项生成的prog.asm输出文件,您可以从生成的asm源构建DLL,如下所示:

    ml.exe /c /coff /Cx prog.asm
    link.exe /nologo /dll /subsystem:console /entry:prog prog.obj kernel32.lib
    
    使用简单的控制台加载程序测试DLL,例如:

    #include <Windows.h>
    int __cdecl main(void)
    {
        HMODULE hLib = LoadLibrary("prog.dll");
        printf("LoadLibrary result: 0x%X / code=0x%X\n",hLib,GetLastError());
    }
    
    编译器生成的MSVC 7.1 ASM文件如下所示,以供参考。请注意该文件如何将自身称为“列表”:


    通常情况下,您无法汇编Microsoft编译器的输出并获得有效的结果。MASM不支持编译器使用的所有功能,因此程序集输出仅代表编译器创建的目标文件。@RossRidge“程序集输出仅代表编译器创建的目标文件”-这应该没有问题,因为我可以直接编译编译器创建的对象文件-我只是不知道如何编译/链接它以使其从asm文件工作。不,事实上,您能够将编译器创建的程序集文件组装到对象文件中并不一定意味着您得到了可以工作的东西。Microsoft编译器不支持您尝试执行的操作。您应该只使用编译器直接创建的对象文件。LLVM工具链支持首先创建.asm,然后组装它。甚至有一个工具和测试集现在确保这相当于直接产生输出(检查CFC),见英伟达NVCC CUDA编译器也可以编译为他们的汇编语言PTX作为中间步骤,然后继续编译,而不是给出一个
    ml.exe /c /coff /Cx prog.asm
    link.exe /nologo /dll /subsystem:console /entry:prog prog.obj kernel32.lib
    
    #include <Windows.h>
    int __cdecl main(void)
    {
        HMODULE hLib = LoadLibrary("prog.dll");
        printf("LoadLibrary result: 0x%X / code=0x%X\n",hLib,GetLastError());
    }
    
    Result of memcmp: 1
    LoadLibrary result: 0x10000000 / code=0x0
    Result of memcmp: 1
    
    ; Listing generated by Microsoft (R) Optimizing Compiler Version 13.10.6030 
    
        TITLE   prog.c
        .386P
    include listing.inc
    if @Version gt 510
    .model FLAT
    else
    _TEXT   SEGMENT PARA USE32 PUBLIC 'CODE'
    _TEXT   ENDS
    _DATA   SEGMENT DWORD USE32 PUBLIC 'DATA'
    _DATA   ENDS
    CONST   SEGMENT DWORD USE32 PUBLIC 'CONST'
    CONST   ENDS
    _BSS    SEGMENT DWORD USE32 PUBLIC 'BSS'
    _BSS    ENDS
    $$SYMBOLS   SEGMENT BYTE USE32 'DEBSYM'
    $$SYMBOLS   ENDS
    _TLS    SEGMENT DWORD USE32 PUBLIC 'TLS'
    _TLS    ENDS
    FLAT    GROUP _DATA, CONST, _BSS
        ASSUME  CS: FLAT, DS: FLAT, SS: FLAT
    endif
    
    INCLUDELIB MSVCRT
    INCLUDELIB OLDNAMES
    
    _DATA   SEGMENT
    $SG74617 DB 'bar', 00H
    $SG74618 DB 'foo', 00H
    $SG74619 DB 'Result of memcmp: %d', 0aH, 00H
    _DATA   ENDS
    PUBLIC  _prog@12
    EXTRN   __imp__printf:NEAR
    EXTRN   _memcmp:NEAR
    ; Function compile flags: /Odt
    _TEXT   SEGMENT
    _hInst$ = 8                     ; size = 4
    _dwReason$ = 12                     ; size = 4
    _dwReserved$ = 16                   ; size = 4
    _prog@12 PROC NEAR
    ; File prog.c
    ; Line 10
        push    ebp
        mov ebp, esp
    ; Line 11
        push    3
        push    OFFSET FLAT:$SG74617
        push    OFFSET FLAT:$SG74618
        call    _memcmp
        add esp, 12                 ; 0000000cH
        push    eax
        push    OFFSET FLAT:$SG74619
        call    DWORD PTR __imp__printf
        add esp, 8
    ; Line 12
        mov eax, 1
    ; Line 13
        pop ebp
        ret 12                  ; 0000000cH
    _prog@12 ENDP
    _TEXT   ENDS
    END