Resolving undefined symbols externally - visual-studio

Beginner level in Assembly.
The error I receive in Visual Studio is:
1>File2.asm(27): error A2006: undefined symbol : sprintf
1>File2.asm(28): error A2006: undefined symbol : MessageBoxA
File 1 is what handles calculations
File 2 is what prints result to a window.
The line the handles print instructions is:
invoke sprintf, addr szBuf, offset $interm, eax, edx
invoke MessageBoxA, 0, addr szBuf, offset _title, 0
invoke ExitProcess, 0
What am I doing wrong to the cause it not to build?
Is it because sprintf is a C function?
File1.asm
.386
.model flat, stdcall
option casemap :none
PUBLIC squareroot
PUBLIC szBuf
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\msvcrt.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\msvcrt.lib
.data
_title db "Result",13,10,0
$interm db "%0.4f","+","%0.5f",13,10,0
Aval REAL8 1.000
Bval REAL8 -2.000
Cval REAL8 19.000
_fourval REAL8 4.000
$Tvalueinc REAL4 1.0,2.00,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0
$sampleval real10 4478784.0
$Powercounter dd ?
squareroot dq ?
$prevCW dw ?
$Tagword dd ?
$INT1 dq ?
EXTERN Finished:PROC
.code
szBuf:
add eax,4
fstcw $prevCW
fwait
fld Bval ; [loads first instance of b]]
fmul Bval ; [b*b = b^2]
fld Aval ;[Load a (a*c)]
fmul Cval ;(a*c)
fmul _fourval ;[4*a*c]
fsubp;[b^2-4*a*c]
ftst ;compare ST(0) with 0.0
fstsw ax ;[store camparison results in ax]
sahf ;transfer flags from AH register
mov ecx, 0004h
jb _negative ;jump if <0
fsqrt ;sqrt(b^2-4*a*c)
_negative:
fchs
fsqrt
fld $sampleval
xor eax,eax
$repeat:
inc eax
push eax
mov ax, $prevCW
push eax
fldcw [esp]
fld $Tvalueinc[ecx]
fdivp
fld st(0)
FRNDINT
fcomp
fstsw ax
Sahf
fnstenv [ebx-10h]
movzx eax, word ptr [ebx-10h + 8h]
fldcw $prevCW
pop eax
pop eax
jz $repeat
dec eax
cmp eax, $Powercounter
add ecx, 0004h
mov eax, dword ptr squareroot
mov edx, dword ptr squareroot[0004h]
jmp Finished
END szBuf
File2.asm
.386
.model flat,stdcall
option casemap:none
PUBLIC Finished
PUBLIC ExitProcess
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\msvcrt.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\msvcrt.lib
.data
_title db "Result",13,10,0
$interm db "%0.4f","+","%0.5f",13,10,0
.code
Finished:
invoke sprintf, addr szBuf, offset $interm, eax, edx
invoke MessageBoxA, 0, addr szBuf, offset _title, 0
invoke ExitProcess, 0
END

You are using the function sprintf from MSVCRT.lib, which is a C library, and its exported name is prefixed by an underscore. Hence it's _sprintf instead of sprintf.
The function MessageBox is contained in user32.lib which you didn't incluce, so the linker could not find it.
There's also the function wsprintf in user32.lib which is quite similar to sprintf, so if you want to save space and decrease the size of the file you can use that one instead.
sprintf and wsprintf both use the C calling convention (contrary to the STDCALL convention set as default in the .model flat,stdcall line).
Note It is important to note that wsprintf uses the C calling convention (_cdecl), rather than the standard call (_stdcall) calling convention. As a result, it is the responsibility of the calling process to pop arguments off the stack, and arguments are pushed on the stack from right to left. In C-language modules, the C compiler performs this task.
But INVOKE(more precise: its PROTO directive) does take care of that so don't worry now.
To fix the errors change/add these lines to your code:
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
...
invoke _sprintf, addr szBuf, offset $interm, eax, edx

Related

WriteConsole returns false in MASM

I am trying to make an array, and access it's values and print them out
After calling the WriteConsole subroutine, it is returning false, however, all the values are supplied. Here we can see that - https://imgur.com/a/vUfwOo6
Eax register is 0 after calling WriteConsole. Here you can see the register values, that are being pushed to the stack. https://imgur.com/a/gv6s4uG
Considering, that WriteConsole is WINAPI subroutine, that means it's stdcall. So, I am passing values right to left.
lpReserved -> 0
lpNumberOfCharsWritten -> offset to 00403028 (CharsWritten variable)
nNumberOfCharsToWrite -> Just 2, because in array only ints are present of length 2
*lpBuffer -> ebx register, which contains array lvalue
hConsoleOutput -> Output from GetStdHandle (In this case -> edx register -> A0)
My MASM code:
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
include C:\masm32\include\masm32.inc
includelib C:\masm32\lib\masm32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\msvcrt.lib ; Some default includes :P
.data
dArray dd 10 dup (?) ; Main array
CharsWritten dd ?
LoopCounter dd 0
StdHandle dd ?
.code
PrintArrayToScreen proc
mov eax, STD_OUTPUT_HANDLE
push eax
call GetStdHandle
mov StdHandle,eax
mov eax,[LoopCounter]
innerPrintLoop:
mov ecx,offset dArray
mov eax, [LoopCounter]
mov ebx,[ecx + eax * 4]
mov esi,offset CharsWritten
push 0
push esi
push 2
push ebx
mov edx,StdHandle
push edx
call WriteConsole
mov eax,[LoopCounter]
inc eax
mov LoopCounter,eax ; Storing the Loop Counter in the variable
cmp eax,11 ; +1 because of loop counter increment
jnz innerPrintLoop
ret
PrintArrayToScreen endp
arrayLoop proc ; Subroutine for the array filling
mov eax,offset dArray
mov bx,10
mov ecx,0
innerLoop:
mov [eax + ecx * 4],bx ; ecx * 4 => counter * 4 bytes
inc bx
add ecx,1
cmp ecx,10
jne innerLoop
mov eax,offset dArray
ret
arrayLoop endp
start:
call arrayLoop
call PrintArrayToScreen
mov eax,0
push eax
call ExitProcess
end start
From the documentation for WriteConsole:
lpBuffer [in]
A pointer to a buffer that contains characters to be written to the console screen buffer.
So you should be passing the address of the data to be written, but you're actually passing the data itself.
You could "fix" that by changing the line mov ebx,[ecx + eax * 4] to lea ebx,[ecx + eax * 4]. But note that WriteConsole doesn't do any integer-to-string conversion for you, so you still probably wouldn't get the result you expected. If you want that sort of functionality, use printf.

Why WriteConsoleW breaks After call CoInitialize using ml64

I am trying to do some Office automation via 64-bit assembly using ml64.exe from Visual Studio 2019. Before I can call the Office COM interfaces I need to call CoInitialize. I am currently just testing initializing COM and writing to the console (I normally don't write assembly code) If I comment out line
call CoInitialize
The WriteConsoleW api call works as expected and outputs message to the screen "COM Failed to Initialize" However as soon as I add call CoInitialize back there is nothing output to console screen, and crash either.
; *************************************************************************
; Proto types for API functions and structures
; *************************************************************************
EXTRN GetStdHandle:PROC
EXTRN WriteConsoleW:PROC
EXTRN CoCreateInstance:PROC
EXTRN CoInitialize:PROC
EXTRN SysFreeString:PROC
EXTRN SysStringByteLen:PROC
EXTRN SysAllocStringByteLen:PROC
EXTRN OleRun:PROC
EXTRN ExitProcess:PROC
.const
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
; *************************************************************************
; Object libraries
; *************************************************************************
includelib user32.lib
includelib kernel32.lib
includelib ole32.lib
includelib oleaut32.lib
; *************************************************************************
; Our data section.
; *************************************************************************
.data
strErrComFailed dw 'C','O','M',' ','F','a','i','l','e','d',' ','t','o',' ','i','n','i','t','i','a','l','i','z','e',0,0
strErrOutlookFailed dw 'F','a','i','l','e','d',' ','t','o',' ','i','n','i','t','i','a','l','i','z','e',' ','O','u','t','l','o','o','k',0,0
; {0006F03A-0000-0000-C000-000000000046}
CLSID_OutlookApplication dd 0006f03ah
dw 0000h
dw 0000h
dw 0C000h
db 00h
db 00h
db 00h
db 00h
db 00h
db 46h
; {00063001-0000-0000-C000-000000000046}
IID_OutlookApplication dd 00063001h
dw 0000h
dw 0000h
dw 0C000h
db 00h
db 00h
db 00h
db 00h
db 00h
db 46h
; {00000000-0000-0000-C000-000000000046}
IID_IUnknown dd 00000000h
dw 0000h
dw 0000h
dw 0C000h
db 00h
db 00h
db 00h
db 00h
db 00h
db 46h
; *************************************************************************
; Our executable assembly code starts here in the .code section
; *************************************************************************
.code
wcslen PROC inputString:QWORD
LOCAL stringLength:QWORD
mov QWORD PTR inputString, rcx
mov QWORD PTR stringLength, 0h
continue:
mov rax, QWORD PTR inputString
mov rcx, QWORD PTR stringLength
movzx eax, word ptr [rax+rcx*2]
test eax, eax
je finished
mov rax, QWORD PTR stringLength
inc rax
mov QWORD PTR stringLength, rax
jmp continue
finished:
mov rax, QWORD PTR stringLength
ret
wcslen ENDP
main PROC
LOCAL hStdOutput:QWORD
LOCAL hErrOutput:QWORD
LOCAL hResult:DWORD
xor ecx,ecx
call CoInitialize
mov DWORD PTR hResult, eax
mov ecx, STD_OUTPUT_HANDLE
call GetStdHandle
mov QWORD PTR hStdOutput, rax
mov ecx, STD_ERROR_HANDLE
call GetStdHandle
mov QWORD PTR hErrOutput, rax
lea rcx,strErrComFailed
call wcslen
mov QWORD PTR [rsp+32], 0
xor r9d, r9d ; lpNumberOfCharsWritten
mov r8d, eax ; nNumberOfCharsToWrite
lea rdx,QWORD PTR strErrComFailed
mov rcx,QWORD PTR hStdOutput
call WriteConsoleW
; When the message box has been closed, exit the app with exit code eax
mov ecx, eax
call ExitProcess
ret
main ENDP
End
Prior to CoInitialize being called WinDbg shows the following register state:
00007ff7`563e1041 e865000000 call Win64App+0x10ab (00007ff7`563e10ab)
0:000> r
rax=00007ff7563e1037 rbx=0000000000000000 rcx=0000000000000000
rdx=00007ff7563e1037 rsi=0000000000000000 rdi=0000000000000000
rip=00007ff7563e1041 rsp=000000a905affa58 rbp=000000a905affa70
r8=000000a9058d6000 r9=00007ff7563e1037 r10=0000000000000000
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl zr na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
Win64App+0x1041:
00007ff7`563e1041 e865000000 call Win64App+0x10ab (00007ff7`563e10ab)
0:000> r ecx
ecx=0
Following CoInitialize being called there is the following register state:
0:000> r
rax=0000000000000000 rbx=0000000000000000 rcx=8aa77f80a0990000
rdx=0000000000000015 rsi=0000000000000000 rdi=0000000000000000
rip=00007ff7563e1046 rsp=000000a905affa58 rbp=000000a905affa70
r8=0000029af97e2620 r9=0000029af97e1440 r10=0000000000000005
r11=000000a905aff9d8 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
Win64App+0x1046:
00007ff7`563e1046 8945ec mov dword ptr [rbp-14h],eax ss:000000a9`05affa5c=00000000
0:000> r eax
eax=0
After calling GetStdHandle :
0:000> r
rax=0000000000000074 rbx=0000000000000000 rcx=0000029af97d2840
rdx=0000000000000015 rsi=0000000000000000 rdi=0000000000000000
rip=00007ff7563e1053 rsp=000000a905affa58 rbp=000000a905affa70
r8=0000029af97e2620 r9=0000029af97e1440 r10=0000000000000005
r11=000000a905aff9d8 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
On call to WriteConsoleW, it seems like parameters are still correct but nothing output to screen:
KERNEL32!WriteConsoleW:
00007ffb`a97028f0 ff258a4c0500 jmp qword ptr [KERNEL32!_imp_WriteConsoleW (00007ffb`a9757580)] ds:00007ffb`a9757580={KERNELBASE!WriteConsoleW (00007ffb`a697b750)}
0:000> du rdx
00007ff7`563e3000 "COM Failed to initialize"
0:000> r
rax=0000000000000018 rbx=0000000000000000 rcx=0000000000000074
rdx=00007ff7563e3000 rsi=0000000000000000 rdi=0000000000000000
rip=00007ffba97028f0 rsp=000000a905affa50 rbp=000000a905affa70
r8=0000000000000018 r9=0000000000000000 r10=0000000000000005
r11=000000a905aff9d8 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl zr na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
KERNEL32!WriteConsoleW:
00007ffb`a97028f0 ff258a4c0500 jmp qword ptr [KERNEL32!_imp_WriteConsoleW (00007ffb`a9757580)] ds:00007ffb`a9757580={KERNELBASE!WriteConsoleW (00007ffb`a697b750)}
I tried using CoInitializeEx instead and had same problem:
mov edx, COINIT_APARTMENTTHREADED ; dwCoInit (COINIT_APARTMENTTHREADED = 2)
xor ecx, ecx ; pvReserved
call CoInitializeEx
x64 ABI require The stack is always 16-byte aligned when a call instruction is executed also 32 byte reserved space. so at every function entry point we will be have:
RSP == 16*N + 8
so we in general must do SUB RSP,40 + N*16 in function body, if we will call another functions.
but when we declare LOCAL variables in function - compiler (masm64) do some stack allocation but not carry about stack align and 32 byte reserved space. so need do this yourself. also when you using LOCAL variables - masm64 use RBP register for save old RSP value and restore it at the end (use leave instruction). and access locals via RBP so you can not change RBP in function yourself.
so code can be next
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
EXTRN __imp_GetStdHandle:QWORD
EXTRN __imp_WriteConsoleW:QWORD
EXTRN __imp_CoInitialize:QWORD
EXTRN __imp_ExitProcess:QWORD
EXTRN __imp__getch:QWORD
EXTRN __imp_wcslen:QWORD
WSTRING macro text:VARARG
FOR arg, <text>
if #InStr( , arg, # )
f = 0
FORC c, <arg>
IF f
DW '&c'
ENDIF
f = 1
ENDM
else
DW &arg
endif
ENDM
DW 0
ENDM
.const
strErrComFailed: WSTRING #----, 13, 10, #Press any key:, 13, 10
.code
maina PROC
LOCAL hStdOutput:QWORD
LOCAL hErrOutput:QWORD
LOCAL hResult:DWORD
sub rsp,32
and rsp,not 15
xor ecx,ecx
call __imp_CoInitialize
mov hResult, eax
mov ecx, STD_OUTPUT_HANDLE
call __imp_GetStdHandle
mov hStdOutput, rax
mov ecx, STD_ERROR_HANDLE
call __imp_GetStdHandle
mov hErrOutput, rax
lea rcx,strErrComFailed
call __imp_wcslen
mov QWORD PTR [rsp+32], 0
xor r9d, r9d ; lpNumberOfCharsWritten
mov r8d, eax ; nNumberOfCharsToWrite
lea rdx,strErrComFailed
mov rcx,hStdOutput
call __imp_WriteConsoleW
call __imp__getch
mov ecx, eax
call __imp_ExitProcess
ret
maina ENDP
END
also some notes:
import functions always called via pointer. all this pointer names begin from __imp_ prefix. so we need declare imported api Xxx as EXTRN __imp_Xxx:QWORD - this more efficient compare PROC declaration - in this case linker need to build stub proc with single jmp instruction:
Xxx proc
jmp __imp_Xxx
Xxx endp
of course better do direct call __imp_Xxx and not have Xxx stub, instead call Xxx - code will be smaller and faster
you not need implement wcslen yourself - you can import it implementation from ntdllp.lib (always) or msvcrt.lib (very depend from concrete lib implementation, but msvcrt.dll of course export wcslen - you can build msvcrt.lib yourself) and use call __imp_wcslen
use declaration like
strErrComFailed dw 'C','O','M',' '...
very uncomfortable. you can use for example such a macro
WSTRING macro text:VARARG
FOR arg, <text>
if #InStr( , arg, # )
f = 0
FORC c, <arg>
IF f
DW '&c'
ENDIF
f = 1
ENDM
else
DW &arg
endif
ENDM
DW 0
ENDM
;strErrComFailed: WSTRING #----, 13, 10, #Press any key:, 13, 10
finally the lpNumberOfCharsWritten parameter in function WriteConsoleW is optional. if you look for declaration is sdk - it declared with __out_opt or _Out_opt_. so you can pass 0 here if not need such info

In assembly, the numbers changed when i assigned them to a register

I am using visual studio for assembly coding and I am pretty new to assembly. My problem is when I assign a number to a register the value of the number is changed.
.386
.model flat, stdcall
stack 4096
ExitProcess PROTO, dwExitCode: DWORD
.data
;define your variables here
adword DWORD ?
aword WORD 7788h
.code
main PROC
mov eax,11223344h ; moved a constant into a register
mov ebx, eax ; moved ebx -> eax
mov adword, eax ; mov eax-> adword
mov ax, aword ; 7788h -> ax
mov ah, 10h
INVOKE ExitProcess, 0
main ENDP
END main
The value of Eax is not 11223344 it becomes something else.

Linking multiple .asm files in Visual Studio

I use visual studio 2017
Lets say I one assembly file named "Factorial.asm" and I break it into two .asm files named "one.asm" and "two.asm":
Factiorial.asm works just fine.
Factorial.asm contains
.386
.model flat, stdcall
option casemap :none
includelib \masm32\lib\msvcrt.lib
sprintf proto C :vararg
includelib \masm32\lib\user32.lib
MessageBoxA proto :ptr,:ptr,:ptr,:DWORD
includelib \masm32\lib\kernel32.lib
ExitProcess proto :dword
.data
format db "%llu", 13, 10, 0
_title db "Result",13,10,0
.code
main PROC
LOCAL szBuf[9]:byte
mov eax, 15 ; initial value (low-order bits)
xor edx, edx ; initial value's high-order bits are 0
mov ecx, eax ; loop counter
Factorial:
dec ecx ; decrement counter
jz Finished ; when counter == 0, we're done
mov ebx, ecx ; make copy of counter
imul ebx, edx ; high-order bits * multiplier
mul ecx ; low-order bits * multiplier
add edx, ebx ; add high-order product to high-order bits of low-order product
cmp ecx, 1
jg Factorial ; keep looping as long as counter > 1
Finished:
invoke sprintf, addr szBuf, offset format, eax, edx
invoke MessageBoxA, 0, addr szBuf, offset _title, 0
invoke ExitProcess, 0
main ENDP
one.asm contains
.386
.model flat, stdcall
option casemap :none
includelib \masm32\lib\msvcrt.lib
sprintf proto C :vararg
includelib \masm32\lib\user32.lib
MessageBoxA proto :ptr,:ptr,:ptr,:DWORD
includelib \masm32\lib\kernel32.lib
ExitProcess proto :dword
.data
format db "%llu", 13, 10, 0
_title db "Result",13,10,0
.code
main PROC
LOCAL szBuf[9]:byte
mov eax, 15 ; initial value (low-order bits)
xor edx, edx ; initial value's high-order bits are 0
mov ecx, eax ; loop counter
Factorial:
dec ecx ; decrement counter
jz Finished ; when counter == 0, we're done
mov ebx, ecx ; make copy of counter
imul ebx, edx ; high-order bits * multiplier
mul ecx ; low-order bits * multiplier
add edx, ebx ; add high-order product to high-order bits of low-order product
cmp ecx, 1
jg Factorial ; keep looping as long as counter > 1
main ENDP
two.asm contains
Finished:
invoke sprintf, addr szBuf, offset format, eax, edx
invoke MessageBoxA, 0, addr szBuf, offset _title, 0
invoke ExitProcess, 0
How would I link "one.asm" and "two.asm" using Visual Studio 2017. Or in other words, call labels from separate .asm files?
Use the directive extrn to declare functions outside of the current source file:
extrn foo:proc
You can optionally use public to declare local functions as public, but I think functions are public by default.
Since VS2015, printf is now inlined with C / C++ code, at least in the case of 64 bit builds. One way to deal with this is to have a C / C++ source file that makes a reference to printf, in which case the assembly code can then access printf. I don't know if this applies to sprintf also. You'll get a link error if this is an issue.
There are usually three types of objects that you need to transfer information across files to.
Proc defined in source file called for in user file : By default, in masm, all procs are global. So, the source file needs no declaration, the user file needs a declaration :
extern <proc_name> : proc
Variable defined in source file and called by a procedure in user file : In this situation, the source file needs a declaration
public <var_name>
and the user file must contain the declaration :
extern <varname> : var_size ; where var_size is word, qword ymmword...
It is NOT mandatory that the declaration size must match the extern size. However, consequences of not ensuring this match are usually expensive. Also, if a variable has been declared as an extern, and subsequently, not used in that file, it still must match a corresponding public declaration in some file, otherwise linker will fail.
Label defined in source file and jumped to from a user file : identical treatment as a variable (case 2) is treated
Trust this helps.

What variables can we define in assembly language using Visual Studio?

This is my code, the program runs fine when I define the third variable as cc, if and when I change it to C, the variable turns blue (indicating that it is a keyword in the assembly language since I have the .dat file installed to highlight assembly language keywords) the debugger gives me an error of ...\Irvine\Examples\ch03\AddSums.asm" exited with code 1.
I have ran the code through breakpoints in the first case too. Everything is good. I am just concerned since this is an assignment for school and the variable needs to be C, why is it not letting me?
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD
.data
A DWORD 30
B DWORD 20
cc DWORD 10
D DWORD 5
.code
main PROC
mov eax, A
mov ebx, B
mov ecx, cc
mov edx, D
add eax, ebx
add ecx, edx
sub eax, ecx
mov A, eax
INVOKE ExitProcess, 0
main ENDP
END main

Resources