Having such a simple assembly Win 32 program:
.386
.model flat, stdcall
option casemap :none
EXTERN printf :PROC ; declare printf
.data
HelloWorld db "Hello Wolrd!:-)", 0
.code
start:
sub esp, 4
push offset HelloWorld
call printf
add esp, 4
ret
end start
I can successfully compile it by just:
ml.exe /c HelloWorld.asm
BUT have a problem linking it. When I use:
link HelloWorld.obj libcmt.lib
I'm getting an error:
unresolved external symbol _main called in _mainCRTStartup
What have I change/correct to to successfully link the program to run it?
P.S.
Please don't tell me to use just nasm. I'd like to use ml & link from my MSVC.
With some minor tweaks this now builds correctly.
.386
.model flat, c
option casemap :none
includelib libcmt.lib
includelib legacy_stdio_definitions.lib
EXTERN printf :PROC ; declare printf
.data
HelloWorld db "Hello World!:-)", 0
.code
main PROC
push offset HelloWorld
call printf
add esp, 4
ret
main ENDP
END
The main edits are
.model flat, c sets the calling conventions for procedures to C.
If you decide to keep .model flat, stdcall it'll require these changes.
Replace
EXTERN printf :PROC
main PROC
with
printf PROTO NEAR C,:DWORD
main PROC NEAR C
Included libcmt.lib and legacy_stdio_definitions.lib which statically links the native C-Runtime startup into your code.
Changed entry point from start to main. There's an entry point (_mainCRTStartup) within the C-Runtime library (CRT) libcmt.lib, which does some initialization tasks, and then hands off control to the entry point for your application main. You can change the default entry point, but usually you want the convenience of the initialization the CRT entry point does for you automatically.
Removed the first sub esp,4 so the remaining one push is balanced by the add esp,4, so ESP is pointing at the return address when ret runs.
To build, open a Windows command prompt and run:
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvars32.bat"
to set the MSVC Environment initialized for: 'x86'
Next, run these MASM commands
ml.exe /c /coff HelloWorld.asm
link.exe /SUBSYSTEM:console HelloWorld.obj
The program displays
Hello World!:-)
Your error message says that it can't find the exported symbol (i.e. function) "_main". I expect renaming your start function to _main would get it to compile.
Related
Having such a simple Win32 assembly program
.386
.model flat, c
option casemap :none
;includelib C:\Users\Darek\Dev\VC\lib\libcmt.lib
;includelib C:\Users\Darek\Dev\VC\lib\legacy_stdio_definitions.lib
EXTERN printf :PROC ; declare printf
.data
HelloWorld db "Hello j World!:-)", 0
.code
main PROC
push offset HelloWorld
call printf
add esp, 4
ret
main ENDP
END
I'd like to change the entry point name from the standard main to say my_start, so I've changed the name of the main function to my_start
...
my_start PROC
...
my_start ENDP
...
and then linked like below
link /ENTRY:my_start /SUBSYSTEM:CONSOLE HelloWorld.obj libcmt.lib
but getting the linker error:
undefined external symbol _main called in _mainCRTStartup
Why does the linker ingres the ENTRY option?
What I'm doing wrong and what to do to get it working?
P.S.
I'm using the ml and link provided with my MSVC 2019
C requires your main program to be called main. That's a restriction imposed by C, not the hardware itself. I've only ever used UASM and x86-16, so I'm not familiar with Win32 syntax, but you might be able to try this:
my_start equ main
(Assuming Win32 has an equ directive that is.)
Having such a simple assembly Win 32 program:
.386
.model flat, stdcall
option casemap :none
EXTERN printf :PROC ; declare printf
.data
HelloWorld db "Hello Wolrd!:-)", 0
.code
start:
sub esp, 4
push offset HelloWorld
call printf
add esp, 4
ret
end start
I can successfully compile it by just:
ml.exe /c HelloWorld.asm
BUT have a problem linking it. When I use:
link HelloWorld.obj libcmt.lib
I'm getting an error:
unresolved external symbol _main called in _mainCRTStartup
What have I change/correct to to successfully link the program to run it?
P.S.
Please don't tell me to use just nasm. I'd like to use ml & link from my MSVC.
With some minor tweaks this now builds correctly.
.386
.model flat, c
option casemap :none
includelib libcmt.lib
includelib legacy_stdio_definitions.lib
EXTERN printf :PROC ; declare printf
.data
HelloWorld db "Hello World!:-)", 0
.code
main PROC
push offset HelloWorld
call printf
add esp, 4
ret
main ENDP
END
The main edits are
.model flat, c sets the calling conventions for procedures to C.
If you decide to keep .model flat, stdcall it'll require these changes.
Replace
EXTERN printf :PROC
main PROC
with
printf PROTO NEAR C,:DWORD
main PROC NEAR C
Included libcmt.lib and legacy_stdio_definitions.lib which statically links the native C-Runtime startup into your code.
Changed entry point from start to main. There's an entry point (_mainCRTStartup) within the C-Runtime library (CRT) libcmt.lib, which does some initialization tasks, and then hands off control to the entry point for your application main. You can change the default entry point, but usually you want the convenience of the initialization the CRT entry point does for you automatically.
Removed the first sub esp,4 so the remaining one push is balanced by the add esp,4, so ESP is pointing at the return address when ret runs.
To build, open a Windows command prompt and run:
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvars32.bat"
to set the MSVC Environment initialized for: 'x86'
Next, run these MASM commands
ml.exe /c /coff HelloWorld.asm
link.exe /SUBSYSTEM:console HelloWorld.obj
The program displays
Hello World!:-)
Your error message says that it can't find the exported symbol (i.e. function) "_main". I expect renaming your start function to _main would get it to compile.
Pretty new to assembly, having fun poking at it. I am wanting to split the functionality of my program across multiple files, specifically by grouping similar functions together for organization. These other files would be called by the main file (and hopefully even other non-main files). I haven't yet managed to do so, and would like help.
I am not using an IDE, preferring to use notepad++, ml.exe, and link.exe (from MASM folder) to write, assemble, and link the program myself. Most online resources I have looked at assume Visual Studio, and give code that doesn't work for me, or maybe is incomplete b/c the IDE does something else. I do not intend to start using an IDE.
I would like to learn the "best" way, meaning, the way that is most useful for future projects. Can I set it up in such a way that I can just copy the file and write a couple lines of code to use it in a different project in the future? Or maybe that's bad practice and I should learn a more standard method instead? I understand this platform is not for opinionated questions, and I'm hoping this question is more factually based than opinion.
All useful info I can think of:
Language: Masm assembly x86
Computer: 64 bit Windows
Code:
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
Terminal output
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 . . .
Now that your question has been updated with a minimal, complete, verifiable example some specific problems can be identified. When you declare a function with PROC there is a language naming and calling convention applied to each function. Not specifying one associates no special processing.
You can specify a default language with the model directive as a second parameter. In both your files you have used:
.model flat
So you haven't associated a default language. You have defined ClearRegs as:
ClearRegs PROC C
[snip]
ClearRegs ENDP
The problem here is that PROC C specifies the C language calling convention and naming convention. With COFF format (32-bit) the C naming convention requires an underscore (_) to be prepended to the beginning of the function name. If you were to generate a MAP file you'd discover that the Function name exported from utils.asm is actually _ClearRegs and not ClearRegs.
There are a number of ways to fix this. You can choose not to add a default language to the .model directive and tell Driver.asm that ClearRegs is defined as a C PROTOtype by changing:
ClearRegs PROTO
to
ClearRegs PROTO C
So now utils.asm is exporting _ClearRegs and Driver.asm is importing _ClearRegs as both sides match and MASM will handle adding the extra underscore. INVOKE ClearRegs will use the naming convention associated with the PROTO statement which says the language is C so it will add the extra _ for you.
This brings up an additional change you can make. An END directive can be used to specify the entry point to your program rather than using /entry:<name> on the linker command line. The entry point has to have a name that starts with an _ to satisfy the linker.
You currently use this in Driver.asm:
PUBLIC _start
_start:
Main PROC
[snip]
Main ENDP
END
And you use /entry:start when linking. You could change this to be:
_Main PROC
[snip]
_Main ENDP
END _Main ; END with a function name tells linker to use _Main as program entry point
When linking you can now remove the /entry option altogether and you don't need the _start label anymore. We can do better though. The entry point called by the MS C Runtime startup assumes the function is following the C language naming and calling convention. What is preferable is to do this:
Main PROC C
[snip]
Main ENDP
END Main ; END with a function name tells linker to use _Main as program entry point
If you intend to make all your functions PROC C then you can avoid specifying C in most places by changing the default language in both Utils.asm and Driver.asm by changing:
.model flat
to:
.model flat, C
This will change the default for PROTO statements, PUBLIC statements specifying a function defined with PROC and PROC statements themselves. Your code in Driver.asm could look like:
.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 could look like:
.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
And you'd link with:
link /debug /subsystem:console /out:Driver.exe Utils.obj Driver.obj \masm32\lib\kernel32.lib
.386
.model flat, c
.stack 100h
printf PROTO arg1:Ptr Byte
.data
msg1 byte "Hello World!", 0Ah, 0
.code
main proc
INVOKE printf, ADDR msg1
ret
main endp
end main
Hi, I am getting the below errors:
I searched around and found someone said that it can be fixed by linking the microsoft runtime library
Can anyone teach me how can I exactly fix it?
Thanks
Severity Code Description Project File Line Suppression State
Error LNK2019 unresolved external symbol _printf referenced in function _main testing C:\Users\Kin\Desktop\assembly\testing\testing\Source.obj 1
Error LNK1120 1 unresolved externals testing C:\Users\Kin\Desktop\assembly\testing\Debug\testing.exe 1
I don't have VS 2017 installed to try this. Important: Make sure you create a Console Application and not a Windows Application. Once you create this project make sure MASM is added to the build customizations. Add an .ASM file to your project.
Take your code and insert the following lines at the top:
includelib libcmt.lib
includelib libvcruntime.lib
includelib libucrt.lib
includelib legacy_stdio_definitions.lib
An explanation as to why these lines are needed in Visual Studio later than 2013 can be found in this Stackoverflow Answer.
You want the C runtime to be the entry point to your console application (which in turn will call your main). Because of this you MUST remove main from the last line that says end main. When you do end main it bypasses the C runtime startup startup. Failure to properly initialize the C runtime will likely lead to the program crashing when you make calls like printf. It should simply be end and not end main.
The final code you should test is:
includelib libcmt.lib
includelib libvcruntime.lib
includelib libucrt.lib
includelib legacy_stdio_definitions.lib
.386
.model flat, c
.stack 100h
printf PROTO arg1:Ptr Byte
.data
msg1 byte "Hello World!", 0Ah, 0
.code
main proc
INVOKE printf, ADDR msg1
ret
main endp
end
Since Visual Studio 2015, printf is now "inlined" into C code. The assembly code to get around this would be complicated. I ended up including a small C source file in the project with a unused call to printf to get around this problem. I don't recall if the generated printf code is parameter count dependent. I just use the same or more parameters in the dummy C source code call to printf than what I use in the assembly code.
I was trying writing a simple command line program in assembly using the Win32 API. I wrote the following, got it to compile and link with zero errors or warnings, and started it from a command line, but nothing appeared at the command prompt. Can anyone see what's wrong or what would make it say "Hello World!" at the command line? The program:
; Block 1
.386
.model flat,stdcall
option casemap:none
; Block 2
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
; Block 3
.data
szMsg db "Hello World!",0
nNumToWrite dd 12
; Block 4
.data?
numwritten dd ?
CommandLine LPSTR ?
; Block 5
.code
start:
invoke GetCommandLine
mov CommandLine,eax
invoke WriteConsole,CommandLine,addr szMsg,nNumToWrite,numwritten,NULL
xor eax,eax
invoke ExitProcess,eax
end start
Are you linking it as a console program or as a GUI program? That's a property of an executable in Win32.