How to replace _asm nop instructions in 64-bit. compiles and works in 32-bit.
I believe that you can use the __nop intrinsic function. This should compile into the appropriate machine instruction for the processor that you are building.
http://msdn.microsoft.com/en-us/library/aa983381(VS.80).aspx
UPDATE:
Here is an example that I just created with VS2008. It compiles for both Win32 and x64 configurations:
#include "stdafx.h"
#include <intrin.h>
int _tmain(int argc, _TCHAR* argv[])
{
// The intrinsic function below will compile to a NOP machine instruction.
// This is the same as _asm nop in the 32-bit compiler.
__nop();
return 0;
}
In case you are wondering, I disassembled the above example and the x64 output was:
wmain proc near
db 66h
nop
nop
xor eax, eax
retn
wmain endp
The Win32 output was:
_main proc near
nop
xor eax, eax
retn
_main endp
You can't use _asm in a 64-bit program. The replacement is intrinsics, a list of instructions is available here. NOP isn't one of them, you'd have to use MASM64, available as vc\bin\x86_amd64\ml64.exe when you've got the 64-bit compilers installed.
Related
I want to get started in MASM in a mixed C++/Assembly way.
I am currently trying to call a standard-library-function (e.g. printf) from a PROC in assembly, that I then call in C++.
I have the code working after I declared printf's signature in my cpp-file. But I do not understand why I have to do this and if I can avoid that.
My cpp-file:
#include <stdio.h>
extern "C" {
extern int __stdcall foo(int, int);
}
extern int __stdcall printf(const char*, ...); // When I remove this line I get Linker-Error "LNK2019: unresolved external symbol"
int main()
{
foo(5, 5);
}
My asm-file:
.model flat, stdcall
EXTERN printf :PROC ; declare printf
.data
tstStr db "Mult: %i",0Ah,"Add: %i",0 ; 0Ah is the backslash - escapes are not supported
.code
foo PROC x:DWORD, y:DWORD
mov eax, x
mov ebx, y
add eax, ebx
push eax
mov eax, x
mul ebx
push eax
push OFFSET tstStr
call printf
ret
foo ENDP
END
Some Updates
In response to the comments I tried to rework the code to be eligible for the cdecl calling-convention. Unfortunatly this did not solve the problem (the code runs fine with the extern declaration, but throws an error without).
But by trial and error i found out, that the extern seems to force external linkage, even though the keyword should not be needed, because external linkage should be the default for function declarations.
I can omit the declaration by using the function in my cpp-code (i.e. if a add a printf("\0"); somewhere in the source file the linker is fine with it and everythings works correctly.
The new (but not really better) cpp-file:
#include <stdio.h>
extern "C" {
extern int __cdecl foo(int, int);
}
extern int __cdecl printf(const char*, ...); // omiting the extern results in a linker error
int main()
{
//printf("\0"); // this would replace the declaration
foo(5, 5);
return 0;
}
The asm-file:
.model flat, c
EXTERN printf :PROC
.data
tstStr db "Mult: %i",0Ah,"Add: %i",0Ah,0 ; 0Ah is the backslash - escapes are not supported
.code
foo PROC
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov ebx, [ebp+12]
add eax, ebx
push eax
mov eax, [ebp+8]
mul ebx
push eax
push OFFSET tstStr
call printf
add esp, 12
pop ebp
ret
foo ENDP
END
My best guess is that this has to do with the fact that Microsoft refactored the C library starting with VS 2015 and some of the C library is now inlined (including printf) and isn't actually in the default .lib files.
My guess is in this declaration:
extern int __cdecl printf(const char*, ...);
extern forces the old legacy libraries to be included in the link process. Those libraries contain the non-inlined function printf. If the C++ code doesn't force the MS linker to include the legacy C library then the MASM code's use of printf will become unresolved.
I believe this is related to this Stackoverflow question and my answer in 2015. If you want to remove extern int __cdecl printf(const char*, ...); from the C++ code you may wish to consider adding this line to your MASM code:
includelib legacy_stdio_definitions.lib
Your MASM code would look like this if you are using CDECL calling convention and mixing C/C++ with assembly:
.model flat, C ; Default to C language
includelib legacy_stdio_definitions.lib
EXTERN printf :PROC ; declare printf
.data
tstStr db "Mult: %i",0Ah,"Add: %i",0 ; 0Ah is the backslash - escapes are not supported
.code
foo PROC x:DWORD, y:DWORD
mov eax, x
mov ebx, y
add eax, ebx
push eax
mov eax, x
mul ebx
push eax
push OFFSET tstStr
call printf
ret
foo ENDP
END
Your C++ code would be:
#include <stdio.h>
extern "C" {
extern int foo(int, int); /* __cdecl removed since it is the default */
}
int main()
{
//printf("\0"); // this would replace the declaration
foo(5, 5);
return 0;
}
The alternative to passing the includelib line in the assembly code is to add legacy_stdio_definitions.lib to the dependency list in the linker options of your Visual Studio project or the command line options if you invoke the linker manually.
Calling Convention Bug in your MASM Code
You can read about the CDECL calling convention for 32-bit Windows code in the Microsoft documentation as well as this Wiki article. Microsoft summarizes the CDECL calling convention as:
On x86 platforms, all arguments are widened to 32 bits when they are passed. Return values are also widened to 32 bits and returned in the EAX register, except for 8-byte structures, which are returned in the EDX:EAX register pair. Larger structures are returned in the EAX register as pointers to hidden return structures. Parameters are pushed onto the stack from right to left. Structures that are not PODs will not be returned in registers.
The compiler generates prologue and epilogue code to save and restore the ESI, EDI, EBX, and EBP registers, if they are used in the function.
The last paragraph is important in relation to your code. The ESI, EDI, EBX, and EBP registers are non-volatile and must be saved and restored by the called function if they are modified. Your code clobbers EBX, you must save and restore it. You can get MASM to do that by using the USES directive in a PROC statement:
foo PROC uses EBX x:DWORD, y:DWORD
mov eax, x
mov ebx, y
add eax, ebx
push eax
mov eax, x
mul ebx
push eax
push OFFSET tstStr
call printf
add esp, 12 ; Remove the parameters pushed on the stack for
; the printf call. The stack needs to be
; properly restored. If not done, the function
; prologue can't properly restore EBX
; (and any registers listed by USES)
ret
foo ENDP
uses EBX tell MASM to generate extra prologue and epilogue code to save EBX at the start and restore EBX when the function does a ret instruction. The generated instructions would look something like:
0000 _foo:
0000 55 push ebp
0001 8B EC mov ebp,esp
0003 53 push ebx
0004 8B 45 08 mov eax,0x8[ebp]
0007 8B 5D 0C mov ebx,0xc[ebp]
000A 03 C3 add eax,ebx
000C 50 push eax
000D 8B 45 08 mov eax,0x8[ebp]
0010 F7 E3 mul ebx
0012 50 push eax
0013 68 00 00 00 00 push tstStr
0018 E8 00 00 00 00 call _printf
001D 83 C4 0C add esp,0x0000000c
0020 5B pop ebx
0021 C9 leave
0022 C3 ret
That's indeed a bit pointless, isn't it?
Linkers are often pretty dumb things. They need to be told that an object file requires printf. Linkers can't figure that out from a missing printf symbol, stupidly enough.
The C++ compiler will tell the linker that it needs printf when you write extern int __stdcall printf(const char*, ...);. Or, and that's the normal way, the compiler will tell the linker so when you actually call printf. But your C++ code doesn't call it!
Assemblers are also pretty dumb. Your assembler clearly fails to tell the linker that it needs printf from C++.
The general solution is not to do complex things in assembly. That's just not what assembly is good for. Calls from C to assembly generally work well, calls the other way are problematic.
Just a test for __cdecl calling convention.
It's a cmake project and only have 1 source file:
#include <stdio.h>
#define CALL_CONVENTION __cdecl
void CALL_CONVENTION f(int a, int b)
{
printf("%d, %d", a, b);
}
int main()
{
f(1, 2);
return 0;
}
I'm using set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /FA") to output the assembly code.
and when I build with cmake -G "Visual Studio 15", It's build a 32bit application, and every thing is in anticipation:
...
; Line 12
push ebp
mov ebp, esp
; Line 13
push 2 ; <------- argument 2
push 1 ; <------- argument 1
call _f ; <------- call function
add esp, 8
; Line 15
xor eax, eax
; Line 16
cmp ebp, esp
call __RTC_CheckEsp
pop ebp
ret 0
_main ENDP
...
You can see the arguments is passed by push 2 and push 1 instructions, it's __cdecl call convention.
But if I'm using cmake -G "Visual Studio 15 Win64" to build a 64bit application, the __cdecl annotation seems not work (arguments not passed by stack):
...
; Line 12
$LN3:
push rdi
sub rsp, 32 ; 00000020H
mov rdi, rsp
mov ecx, 8
mov eax, -858993460 ; ccccccccH
rep stosd
; Line 13
mov edx, 2 ; <------ argument 2
mov ecx, 1 ; <------ argument 1
call f ; <------ call function
; Line 15
xor eax, eax
; Line 16
add rsp, 32 ; 00000020H
pop rdi
ret 0
main ENDP
...
the arguments is passed by register edx and ecx, not passed by stack.
So why arguments not passed by stack in x64 even if I specify __cdecl and what should I do if I want do same things in x64 environments.
x64 has it's own calling conventions.
Microsoft docs __cdecl
On ARM and x64 processors, __cdecl is accepted but typically ignored by the compiler. By convention on ARM and x64, arguments are passed in registers when possible, and subsequent arguments are passed on the stack. In x64 code, use __cdecl to override the /Gv compiler option and use the default x64 calling convention.
Microsoft docs x64 calling convention
The x64 Application Binary Interface (ABI) uses a four-register fast-call calling convention by default. Space is allocated on the call stack as a shadow store for callees to save those registers. There's a strict one-to-one correspondence between the arguments to a function call and the registers used for those arguments. Any argument that doesn’t fit in 8 bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference.
...
Integer arguments are passed in registers RCX, RDX, R8, and R9
You can see it using ECX and EDX for the int a and int b (as they are 32bits, while the full RCX and RDX is 64bits).
__stdcall, __fastcall and __thiscall are also ignored. __vectorcall is available (the /Gv switch makes it default) and is another register calling convention, but compared to the x64 default it can use registers in more cases and has some other rule differences.
I am trying to create procedure in assembly x86 inside a C++ program. My code is:
#include <stdio.h>
#include <stdlib.h>
int main(void){
_asm{
input1 PROC
push inputnumber
lea eax, inputmsg
push eax
call printf
add esp, 8
push ebx
lea eax, format
push eax
call scanf
add esp, 8
jmp check1
ret
input1 ENDP
}
}
However, when I try to compile the program with Visual studio I get the following error:
C2400 inline assembler syntax error in 'opcode'; found 'PROC'
C2400 inline assembler syntax error in 'opcode'; found 'ENDP'
I've read online but I cannot resolve it. Any suggestions how to fix it ?
Surprised that those are the only errors you get. PROC and ENDP are not recognized by the C inline assembler. Anyway, defining a function inside a function in C isn't a good idea. Try
int main(){
_asm{
push inputnumber
lea eax, inputmsg
:
call scanf
add esp, 8
ret
}
}
You will then end up with a whole bunch of undeclared variables and possibly warnings about scanf if you're using one of the MS compilers.
I have a Visual Studio project that I want to build in both 32 and 64 bit variants. It is 99% written in C/C++, but has one function written in assembler. In 32 bit, I have the asm code in a cpp file as follows:
#ifndef X64
__declspec( naked ) void ERR ( )
{
SeqNum = 0;
SeqTimeStamp++;
__asm
{
mov EAX, MinusTwo
mov EBP, SaveBP
sub EBP, 4
mov ESP, EBP
pop EBP
ret 8
}
}
#endif
The referenced globals are defined at the top of the same file as follows:
extern "C"
{
DWORD SeqTimeStamp, SeqNum;
void *SaveBP;
}
This compiles and builds fine. (And works, too! :-) )
For the 64-bit build, with no inline asm support, the same basic algorithm is coded in a .ASM file. I have Visual Studio (2010) building this file just fine, and including it in the call to link. That code looks like this:
EXTERN SaveBP:PTR
EXTERN SeqNum:DWORD
EXTERN SeqTimeStamp:DWORD
.CODE
ERR PROC PUBLIC FRAME
PUSH RBP
MOV RBP, RSP
.ENDPROLOG
MOV SeqNum, 0
MOV EAX, SeqTimeStamp
INC EAX
MOV SeqTimeStamp, EAX
MOV RAX, 0FFFFFFFEh
MOV RBP, SaveBP
LEA RSP,[RBP+0]
POP RBP
RET 0
ERR ENDP
END
I get a single undefined external in this build:
ERR.obj : error LNK2019: unresolved external symbol SaveBP referenced in function ERR
I've tried a number of different ways of declaring and referencing SaveBP, but I haven't found a winning combination. Has anyone else run into a similar situation, or might know how to solve it?
I've been writing a simple c++ program that uses Assembly to take the GCD of 2 numbers and output them as an example used in a tutorial I watched. I understand what it's doing, but I don't understand why it won't work.
EDIT: Should add that when it runs, it doesn't output anything at all.
#include <iostream>
using namespace std;
int gcd(int a, int b)
{
int result;
_asm
{
push ebp
mov ebp, esp
mov eax, a
mov ebx, b
looptop:
cmp eax, 0
je goback
cmp eax, ebx
jge modulo
xchg eax, ebx
modulo:
idiv ebx
mov eax, edx
jmp looptop
goback:
mov eax, ebx
mov esp, ebp
pop ebp
mov result, edx
}
return result;
}
int main()
{
cout << gcd(46,90) << endl;
return 0;
}
I'm running it on a 32bit Windows system, any help would be appreciated. When compiling, I get 4 errors:
warning C4731: 'gcd' : frame pointer register 'ebp' modified by inline assembly code
warning C4731: 'gcd' : frame pointer register 'ebp' modified by inline assembly code
warning C4731: 'main' : frame pointer register 'ebp' modified by inline assembly code
warning C4731: 'main' : frame pointer register 'ebp' modified by inline assembly code
The compiler will insert these or equivalent instructions for you at the beginning and end of the function:
push ebp
mov ebp, esp
...
mov esp, ebp
pop ebp
If you add them manually, you won't be able to access the function's parameters through ebp, which is why the compiler is issuing warnings.
Remove these 4 instructions.
Also, start using the debugger. Today.