如何在 MASM 中为一个项目编写和组合多个源文件?
How to write and combine multiple source files for a project in MASM?
刚接触汇编,玩得很开心。我想将我的程序的功能拆分到多个文件中,特别是通过将相似的功能组合在一起进行组织。这些其他文件将由主文件调用(希望甚至是其他非主文件)。我还没有成功,希望得到帮助。
我没有使用 IDE,我更喜欢使用 notepad++,ml.exe,和 link.exe(来自 MASM 文件夹)来写,assemble,和 link 程序自己。我看过的大多数在线资源都假设 Visual Studio,并提供对我不起作用的代码,或者可能不完整 b/c IDE 做了其他事情。我不打算开始使用 IDE.
我想学习“最佳”方式,即对未来项目最有用的方式。我是否可以将其设置为仅复制文件并编写几行代码以便将来在不同的项目中使用它?或者这可能是不好的做法,我应该学习一种更标准的方法来代替?我知道这个平台不适合自以为是的问题,我希望这个问题比观点更基于事实。
我能想到的所有有用信息:
语言:Masm 汇编 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
Driver.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
Utils.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 . . .
现在您的问题已更新为最小的、完整的、可验证的示例,可以确定一些具体问题。当您使用 PROC
声明一个函数时,每个函数都会应用一种语言命名和调用约定。不指定一个关联没有特殊处理。
您可以使用模型指令作为第二个参数指定默认语言。在您使用的两个文件中:
.model flat
所以您还没有关联默认语言。您已将 ClearRegs
定义为:
ClearRegs PROC C
[snip]
ClearRegs ENDP
这里的问题是PROC C
指定了C语言的调用约定和命名约定。对于 COFF 格式(32 位),C 命名约定要求在函数名称的开头添加下划线 (_
)。如果您要生成 MAP 文件,您会发现从 utils.asm 导出的函数名称实际上是 _ClearRegs
而不是 ClearRegs
.
有多种方法可以解决此问题。您可以选择不向 .model
指令添加默认语言,并通过更改告诉 Driver.asm
ClearRegs
定义为 C 原型:
ClearRegs PROTO
至
ClearRegs PROTO C
所以现在 utils.asm 正在导出 _ClearRegs
而 Driver.asm 正在导入 _ClearRegs
因为双方匹配并且 MASM 将处理添加额外的下划线。 INVOKE ClearRegs
将使用与 PROTO
语句关联的命名约定,该语句表示语言是 C 因此它将为您添加额外的 _
。
这带来了您可以进行的额外更改。 END
指令可用于指定程序的入口点,而不是在 linker 命令行上使用 /entry:<name>
。入口点的名称必须以 _
开头以满足 linker.
您目前在 Driver.asm:
PUBLIC _start
_start:
Main PROC
[snip]
Main ENDP
END
并且在 linking 时使用 /entry:start
。您可以将其更改为:
_Main PROC
[snip]
_Main ENDP
END _Main ; END with a function name tells linker to use _Main as program entry point
当 linking 时,您现在可以完全删除 /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.asm[= 中的默认语言来避免在大多数地方指定 C
94=] 和 Driver.asm 通过更改:
.model flat
至:
.model flat, C
这将更改 PROTO
语句的默认值,PUBLIC
语句指定用 PROC
和 PROC
语句本身定义的函数。 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 有:
link /debug /subsystem:console /out:Driver.exe Utils.obj Driver.obj \masm32\lib\kernel32.lib
刚接触汇编,玩得很开心。我想将我的程序的功能拆分到多个文件中,特别是通过将相似的功能组合在一起进行组织。这些其他文件将由主文件调用(希望甚至是其他非主文件)。我还没有成功,希望得到帮助。
我没有使用 IDE,我更喜欢使用 notepad++,ml.exe,和 link.exe(来自 MASM 文件夹)来写,assemble,和 link 程序自己。我看过的大多数在线资源都假设 Visual Studio,并提供对我不起作用的代码,或者可能不完整 b/c IDE 做了其他事情。我不打算开始使用 IDE.
我想学习“最佳”方式,即对未来项目最有用的方式。我是否可以将其设置为仅复制文件并编写几行代码以便将来在不同的项目中使用它?或者这可能是不好的做法,我应该学习一种更标准的方法来代替?我知道这个平台不适合自以为是的问题,我希望这个问题比观点更基于事实。
我能想到的所有有用信息:
语言:Masm 汇编 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
Driver.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
Utils.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 . . .
现在您的问题已更新为最小的、完整的、可验证的示例,可以确定一些具体问题。当您使用 PROC
声明一个函数时,每个函数都会应用一种语言命名和调用约定。不指定一个关联没有特殊处理。
您可以使用模型指令作为第二个参数指定默认语言。在您使用的两个文件中:
.model flat
所以您还没有关联默认语言。您已将 ClearRegs
定义为:
ClearRegs PROC C
[snip]
ClearRegs ENDP
这里的问题是PROC C
指定了C语言的调用约定和命名约定。对于 COFF 格式(32 位),C 命名约定要求在函数名称的开头添加下划线 (_
)。如果您要生成 MAP 文件,您会发现从 utils.asm 导出的函数名称实际上是 _ClearRegs
而不是 ClearRegs
.
有多种方法可以解决此问题。您可以选择不向 .model
指令添加默认语言,并通过更改告诉 Driver.asm
ClearRegs
定义为 C 原型:
ClearRegs PROTO
至
ClearRegs PROTO C
所以现在 utils.asm 正在导出 _ClearRegs
而 Driver.asm 正在导入 _ClearRegs
因为双方匹配并且 MASM 将处理添加额外的下划线。 INVOKE ClearRegs
将使用与 PROTO
语句关联的命名约定,该语句表示语言是 C 因此它将为您添加额外的 _
。
这带来了您可以进行的额外更改。 END
指令可用于指定程序的入口点,而不是在 linker 命令行上使用 /entry:<name>
。入口点的名称必须以 _
开头以满足 linker.
您目前在 Driver.asm:
PUBLIC _start
_start:
Main PROC
[snip]
Main ENDP
END
并且在 linking 时使用 /entry:start
。您可以将其更改为:
_Main PROC
[snip]
_Main ENDP
END _Main ; END with a function name tells linker to use _Main as program entry point
当 linking 时,您现在可以完全删除 /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.asm[= 中的默认语言来避免在大多数地方指定 C
94=] 和 Driver.asm 通过更改:
.model flat
至:
.model flat, C
这将更改 PROTO
语句的默认值,PUBLIC
语句指定用 PROC
和 PROC
语句本身定义的函数。 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 有:
link /debug /subsystem:console /out:Driver.exe Utils.obj Driver.obj \masm32\lib\kernel32.lib