在汇编程序中链接到 Kernel32.lib

Luc*_*s S 2 assembly winapi nasm

我今天开始学习汇编,并在 linux 上运行了许多测试,效果很好!我转移到我的电脑上并开始尝试在这里写一些。我在尝试调用外部函数时遇到了一个问题(这在 linux 上也能正常工作),我会得到 LINK 2001 Unresolved External 错误,告诉我在使用 nasm 编译后未定义 WriteConsoleA:

nasm -f win32 test.asm -o test.obj
Run Code Online (Sandbox Code Playgroud)

并使用 cl.exe:

cl test.obj /link libcmt.lib kernel32.lib
Run Code Online (Sandbox Code Playgroud)

我收到这些错误:

test.obj : error LNK2001: unresolved external symbol ExitProcess
test.obj : error LNK2001: unresolved external symbol GetStdHandle
test.obj : error LNK2001: unresolved external symbol WriteConsoleA
test.exe : fatal error LNK1120: 3 unresolved externals
Run Code Online (Sandbox Code Playgroud)

大会:

extern ExitProcess, GetStdHandle, WriteConsoleA
NULL equ 0
STD_OUTPUT_HANDLE equ -11
section .data
   msg db "Hello world!",0xa
msgLen equ $-msg
section .bss
    dummy resd 1
section .text
    global _main
_main:
    push STD_OUTPUT_HANDLE
    call GetStdHandle
    push NULL
    push dummy
    push msgLen
    push msg
    push eax
    call WriteConsoleA
    push NULL
    call ExitProcess
Run Code Online (Sandbox Code Playgroud)

几乎完全从这里复制。任何帮助深表感谢!谢谢!

Gun*_*ner 6

首先,cl 不是链接器,而是编译器。为什么不像我在您链接到的帖子中显示的那样使用 GoLink?使用起来要容易得多。您可以使用 ld 作为链接器,但您的 extern 会发生一些变化。

Windows API 函数使用函数名修饰 - 下划线 + FunctionName + @sizeof 参数,这是链接器的事情。

因此,ExitProcess 实际上是作为 _ExitProcess@4 导出的,因为它需要 1 个 DWORD 参数。WriteConsoleA 需要 5 个 DWORD 大小的参数,所以它将是 _WriteConsole@20

将您的代码更改为:

extern _ExitProcess@4, _GetStdHandle@4, _WriteConsoleA@20

%define ExitProcess _ExitProcess@4
%define GetStdHandle _GetStdHandle@4
%define WriteConsoleA _WriteConsoleA@20

NULL equ 0
STD_OUTPUT_HANDLE equ -11
section .data
   msg db "Hello world!",0xa   
msgLen equ $-msg

section .bss
    dummy resd 1

section .text
    global _main
_main:
    push STD_OUTPUT_HANDLE
    call GetStdHandle

    push NULL
    push dummy
    push msgLen
    push msg
    push eax
    call WriteConsoleA

    push NULL
    call ExitProcess
Run Code Online (Sandbox Code Playgroud)

要与 ld 链接,请告诉它 lib 目录在哪里,最好是: -L "C:\Program Files\Microsoft SDKs\Windows\v6.0\Lib"

然后只需将-l标志与库 -l kernel32 一起使用

我的 makefile 使用 NASM 和 ld 作为您的示例代码:

APP= Sample

all: $(APP) clean

$(APP): $(APP).obj
    "C:\MinGW\bin\ld" $(APP).obj -o $(APP).exe -L "C:\Program Files\Microsoft SDKs\Windows\v6.0\Lib" -l kernel32

$(APP).obj: $(APP).asm
    nasm -f win32 $(APP).asm -o $(APP).obj

clean:
    rm $(APP).obj
Run Code Online (Sandbox Code Playgroud)

如果您像在另一篇文章中那样使用 GoLink,则可以使用文档中显示的 API 函数名称。