首页 > 解决方案 > 如何在 MASM 中为一个项目编写和组合多个源文件?

问题描述

组装很新,玩得很开心。我想将我的程序的功能拆分到多个文件中,特别是通过将类似的功能组合在一起进行组织。这些其他文件将由主文件调用(希望甚至是其他非主文件)。我还没有做到这一点,希望得到帮助。

我没有使用 IDE,我更喜欢使用 notepad++、ml.exe 和 link.exe(来自 MASM 文件夹)来自己编写、组装和链接程序。我看过的大多数在线资源都假设 Visual Studio,并提供对我不起作用的代码,或者可能是不完整的 b/c IDE 执行其他操作。我不打算开始使用 IDE。

我想学习“最好的”方式,意思是对未来项目最有用的方式。我可以这样设置它,以便我可以复制文件并编写几行代码以便将来在不同的项目中使用它吗?或者也许这是不好的做法,我应该学习一种更标准的方法?我知道这个平台不是针对固执的问题,我希望这个问题比意见更基于事实。

我能想到的所有有用信息:
语言:Masm assembly x86
计算机:64 位 Windows

代码:

RUN.bat

@echo off

ml /c /coff /Zi /Fl Driver.asm
ml /c /coff /Zi /Fl Utils.asm

link /debug /subsystem:console /entry:start /out:Driver.exe Utils.obj Driver.obj \masm32\lib\kernel32.lib

Driver.exe

pause

驱动程序.asm

.386
.model flat
.stack 100h

ExitProcess PROTO Near32 STDCALL, dwExitCode:DWORD
ClearRegs PROTO

.DATA
.CODE

PUBLIC _start
_start:

    Main PROC
        MOV EAX, 0
        INVOKE ClearRegs
        INVOKE ExitProcess, 0
    Main ENDP

END

实用程序.asm

.386
.model flat
.stack 100h

OPTION PROC:PRIVATE ; Set procedures to private by default

PUBLIC ClearRegs

.DATA
.CODE
    
    ClearRegs PROC C
        XOR EAX, EAX
        XOR EBX, EBX
        XOR ECX, ECX
        XOR EDX, EDX
        XOR ESI, ESI
        XOR EDI, EDI
        RET
    ClearRegs ENDP

END

终端输出

Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: Driver.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: Utils.asm
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

Driver.obj : error LNK2001: unresolved external symbol ClearRegs
Driver.exe : fatal error LNK1120: 1 unresolved externals
'Driver.exe' is not recognized as an internal or external command,
operable program or batch file.
Press any key to continue . . .

标签: winapiassemblyx86linkermasm

解决方案


现在您的问题已经更新为一个最小的、完整的、可验证的示例,可以识别一些特定的问题。当你声明一个函数时PROC,每个函数都有一个语言命名和调用约定。未指定一关联者无特殊处理。

您可以使用模型指令作为第二个参数指定默认语言。在您使用的两个文件中:

.model flat

所以你没有关联默认语言。您已定义ClearRegs为:

ClearRegs PROC C
[snip]
ClearRegs ENDP

这里的问题是PROC C指定了C语言的调用约定和命名约定。对于 COFF 格式(32 位),C命名约定要求_在函数名称的开头添加下划线 ( )。如果您要生成一个 MAP 文件,您会发现从utils.asm导出的函数名称实际上是_ClearRegs而不是ClearRegs.

有很多方法可以解决这个问题。您可以选择不向.model指令添加默认语言,并通过更改将Driver.asmClearRegs定义为C PROTOtype:

ClearRegs PROTO

ClearRegs PROTO C

所以现在utils.asm正在导出_ClearRegsDriver.asm正在导入_ClearRegs,因为双方都匹配,MASM 将处理添加额外的下划线。将使用与该语言为C的语句INVOKE ClearRegs关联的命名约定,因此它将为您添加额外的内容。PROTO_

这带来了您可以进行的额外更改。指令可END用于指定程序的入口点,而不是/entry:<name>在链接器命令行上使用。入口点必须有一个以 an 开头的名称_才能满足链接器的要求。

您当前在Driver.asm中使用它:

PUBLIC _start
_start:

Main PROC
    [snip]
Main ENDP

END

并且您/entry:start在链接时使用。您可以将其更改为:

_Main PROC
    [snip]
_Main ENDP

END _Main   ; END with a function name tells linker to use _Main as program entry point

链接时,您现在/entry可以完全删除该选项,并且不再需要_start标签。不过我们可以做得更好。MS C运行时启动调用的入口点假定函数遵循C语言命名和调用约定。最好这样做:

Main PROC C
    [snip]
Main ENDP

END Main   ; END with a function name tells linker to use _Main as program entry point

如果您打算创建所有功能PROC C,则可以通过更改Utils.asmDriver.asmC中的默认语言来避免在大多数地方指定:

.model flat

到:

.model flat, C

这将更改PROTO语句的默认值、PUBLIC指定用定义的函数的语句PROCPROC语句本身。您在Driver.asm中的代码可能如下所示:

.386
.model flat, C
.stack 100h

ExitProcess PROTO Near32 STDCALL, dwExitCode:DWORD
ClearRegs PROTO

.DATA
.CODE

Main PROC
    MOV EAX, 0
    INVOKE ClearRegs
    INVOKE ExitProcess, 0
Main ENDP

END Main

Utils.asm可能看起来像:

.386
.model flat, C
.stack 100h

OPTION PROC:PRIVATE ; Set procedures to private by default

PUBLIC ClearRegs

.DATA
.CODE
    
ClearRegs PROC
    XOR EAX, EAX
    XOR EBX, EBX
    XOR ECX, ECX
    XOR EDX, EDX
    XOR ESI, ESI
    XOR EDI, EDI
    RET
ClearRegs ENDP

END

你会链接到:

link /debug /subsystem:console /out:Driver.exe Utils.obj Driver.obj \masm32\lib\kernel32.lib

推荐阅读