I started learning assembly today and have ran many tests on linux that worked very well! I moved over to my PC and started to attempt to write some here. I ran into a problem when attempting to call external functions (which again, worked fine on linux) where I would get LINK 2001 Unresolved External errors telling me that WriteConsoleA is not defined when after compiling with nasm:
nasm -f win32 test.asm -o test.obj
and with cl.exe:
cl test.obj /link libcmt.lib kernel32.lib
I get these errors:
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
The assembly:
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
Copied almost exactly from here.
Any help is much appreciated! Thanks!
First off, cl is not a linker but a compiler. Why not just use GoLink as I shown in the post you linked to? It is much easier to use. You could use ld as the linker, but your externs will change a bit.
Windows API functions use function name decorations - an underscore + FunctionName + @sizeof parameters, this is a linker thing.
So, ExitProcess is really exported as _ExitProcess@4 since it takes 1 DWORD parameter. WriteConsoleA takes 5 parameters DWORD sized, so it would be _WriteConsole@20
Change your code to:
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
To link with ld, tell it where the lib directory is, a good bet is:
-L "C:\Program Files\Microsoft SDKs\Windows\v6.0\Lib"
then just use the -l
flag with the library
-l kernel32
My makefile using NASM and ld for your sample code:
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
If you were to use GoLink as in the other post, you could just use the API Function names as they appear in the docs.