I was trying to reduce the clutter in my original question (below), but I am afraid that made it harder to follow. So here is the original source along with IDA's disassembly.
My question still is this: why does getStruct() pop the return argument and only the return argument off the stack? (It's calling ret 4 instead of ret for no arguments or ret 12 for all three arguments).
#include <iostream>
struct SomeStruct {
char m_buff[0x1000];
};
SomeStruct getStruct(uint32_t someArg1, uint32_t someArg2)
{
return SomeStruct();
}
int main(int argc, const char * argv[])
{
SomeStruct myLocalStruct = getStruct(0x20,0x30);
return 0;
}
; _DWORD __stdcall getStruct(unsigned int, unsigned int)
public getStruct(unsigned int, unsigned int)
getStruct(unsigned int, unsigned int) proc near ; CODE XREF: _main+4Dp
var_8 = dword ptr -8
var_4 = dword ptr -4
arg_0 = dword ptr 8
arg_4 = dword ptr 0Ch
arg_8 = dword ptr 10h
push ebp
mov ebp, esp
sub esp, 18h
mov eax, [ebp+arg_8]
mov ecx, [ebp+arg_4]
mov edx, [ebp+arg_0]
mov [ebp+var_4], ecx
mov [ebp+var_8], eax
mov eax, esp
mov [eax], edx
mov dword ptr [eax+4], 1000h
call ___bzero
add esp, 18h
pop ebp
retn 4
getStruct(unsigned int, unsigned int) endp
; ---------------------------------------------------------------------------
align 10h
; =============== S U B R O U T I N E =======================================
; Attributes: bp-based frame
; int __cdecl main(int argc, const char **argv, const char **envp)
public _main
_main proc near
var_1020 = dword ptr -1020h
var_101C = dword ptr -101Ch
var_1018 = dword ptr -1018h
var_14 = dword ptr -14h
var_10 = dword ptr -10h
var_C = dword ptr -0Ch
argc = dword ptr 8
argv = dword ptr 0Ch
envp = dword ptr 10h
push ebp
mov ebp, esp
push edi
push esi
sub esp, 1030h
mov eax, [ebp+argv]
mov ecx, [ebp+argc]
lea edx, [ebp+var_1018]
mov esi, 20h
mov edi, 30h
mov [ebp+var_C], 0
mov [ebp+var_10], ecx
mov [ebp+var_14], eax
mov [esp], edx ; ptr to destination
mov dword ptr [esp+4], 20h ; unsigned int
mov dword ptr [esp+8], 30h
mov [ebp+var_101C], esi
mov [ebp+var_1020], edi
call getStruct(uint,uint)
sub esp, 4
mov eax, 0
add esp, 1030h
pop esi
pop edi
pop ebp
retn
_main endp
Original question below:
I have some function with the following declaration:
SomeStruct getStruct(uint32_t someArg1, uint32_t someArg2);
getStruct is being called like this:
myLocalStruct = getStruct(someArg1,someArg2);
When compiling this using clang on x86 the calling code looks roughly like this:
lea esi, [ebp-myLocalStructOffset]
mov [esp], esi
mov [esp+4], someArg1
mov [esp+8], someArg2
call getStruct;
sub esp, 4;
So the caller is restoring its stack pointer after the call. Sure enough, the implementation of getStruct ends with a ret 4, effectively popping the structs pointer.
This looks like it is partially cdecl with the caller being responsible for the stack cleanup, and partially stdcall with the callee removing the arguments. I just cannot figure out what the reason is for this approach. Why not leave all the cleanup to the caller? Is there any benefit to this ?
It looks as if you forgot to quote the few lines of assembler above the part you quoted. I assume there is something like:
sub esp,12
somewhere above what you quoted. The calling convention looks like pure stdcall, and the return value is in reality passed as a hidden pointer argument, i.e. the code is in fact compiled as if you had declared:
void __stdcall getStruct(SomeStruct *returnValue, uint32_t someArg1, uint32_t someArg2);
Related
Example:
Dim x As Integer, y As Integer
Input "x=", x
y = x ^ 3 + 3 * x ^ 2 - 24 * x + 30
Print y
End
When I used FreeBasic compiler to generate the assembly code of this source code, I found
.globl _main
_main:
and
call ___main
in assembly code. In addition, it looks like that the Input statement is compiled as
call _fb_ConsoleInput#12
and
call _fb_InputInt#4
The "^" operator is compiled as
call _pow
(I am not sure whether the math function library of FreeBasic is integrated or external)
and the Print statement is compiled as
call _fb_PrintInt#12
and the End statement is compiled as
call _fb_End#4
The question is: How is FreeBasic source code compiled? Why _main and ___main appeared in assembly code? Are I/O statements compiled as function calls?
Reference: Assembly code generated by FreeBasic compiler
.intel_syntax noprefix
.section .text
.balign 16
.globl _main
_main:
push ebp
mov ebp, esp
and esp, 0xFFFFFFF0
sub esp, 20
mov dword ptr [ebp-4], 0
call ___main
push 0
push dword ptr [ebp+12]
push dword ptr [ebp+8]
call _fb_Init#12
.L_0002:
mov dword ptr [ebp-8], 0
mov dword ptr [ebp-12], 0
push -1
push 0
push 2
push offset _Lt_0004
call _fb_StrAllocTempDescZEx#8
push eax
call _fb_ConsoleInput#12
lea eax, [ebp-8]
push eax
call _fb_InputInt#4
push dword ptr [_Lt_0005+4]
push dword ptr [_Lt_0005]
fild dword ptr [ebp-8]
sub esp,8
fstp qword ptr [esp]
call _pow
add esp, 16
fild dword ptr [ebp-8]
fild dword ptr [ebp-8]
fxch st(1)
fmulp
fmul qword ptr [_Lt_0005]
fxch st(1)
faddp
mov eax, dword ptr [ebp-8]
imul eax, 24
push eax
fild dword ptr [esp]
add esp, 4
fxch st(1)
fsubrp
fadd qword ptr [_Lt_0006]
fistp dword ptr [ebp-12]
push 1
push dword ptr [ebp-12]
push 0
call _fb_PrintInt#12
push 0
call _fb_End#4
.L_0003:
push 0
call _fb_End#4
mov eax, dword ptr [ebp-4]
mov esp, ebp
pop ebp
ret
.section .data
.balign 4
_Lt_0004: .ascii "x=\0"
.balign 8
_Lt_0005: .quad 0x4008000000000000
.balign 8
_Lt_0006: .quad 0x403E000000000000
Yes, things like PRINT are implemented as function calls, though i am not sure why this matters to you unless you are currently learning assembly.
As for _main, that is the ASM name for the main() C function used as the main program.
On x86, it is common for global/exported function names in C to be preceded by _ in the ASM output.
___main is the ASM name for the __main() C function called by the MinGW C runtime library startup code before anything in _main is executed.
Again, you'll see the extra _ preceding the C function name.
After that is a call to fb_Init(argc, argv, FB_LANG_FB) to initialize the FreeBASIC runtime library with the default "fb" FreeBASIC dialect and argc elements in the argument vector argv.
The #12 means the argument list is 12 bytes long (e.g., 4+4+4=12 as with fb_Init here); see __stdcall | Microsoft Docs for more information on that.
When using assembly language with MASM (x86 architecture), one can make use of the standard C functions by including libraries. For example: printf and getchar.
When compiling with Asembly With Source Code/FAs in Visual Studio and inspecting the resulting assembly file I stumbled upon the following:
PUBLIC _printf
EXTRN __imp__getchar : PROC
_printf is declared PUBLIC and defined locally (inline within the same file, thus not defined externally in the library file), while _imp_getchar is defined externally
This is the resulting _printf definition the compiler generated while compiling in debug:
_TEXT SEGMENT
__ArgList$ = -20 ; size = 4
__Result$ = -8 ; size = 4
__Format$ = 8 ; size = 4
_printf PROC ; COMDAT
; 950 : {
push ebp
mov ebp, esp
sub esp, 216 ; 000000d8H
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-216]
mov ecx, 54 ; 00000036H
mov eax, -858993460 ; ccccccccH
rep stosd
; 951 : int _Result;
; 952 : va_list _ArgList;
; 953 : __crt_va_start(_ArgList, _Format);
call ??$__vcrt_va_start_verify_argument_type#QBD##YAXXZ ; __vcrt_va_start_verify_argument_type<char const * const>
lea eax, DWORD PTR __Format$[ebp+4]
mov DWORD PTR __ArgList$[ebp], eax
; 954 : _Result = _vfprintf_l(stdout, _Format, NULL, _ArgList);
mov eax, DWORD PTR __ArgList$[ebp]
push eax
push 0
mov ecx, DWORD PTR __Format$[ebp]
push ecx
mov esi, esp
push 1
call DWORD PTR __imp____acrt_iob_func
add esp, 4
cmp esi, esp
call __RTC_CheckEsp
push eax
call __vfprintf_l
add esp, 16 ; 00000010H
mov DWORD PTR __Result$[ebp], eax
; 955 : __crt_va_end(_ArgList);
mov DWORD PTR __ArgList$[ebp], 0
; 956 : return _Result;
mov eax, DWORD PTR __Result$[ebp]
; 957 : }
pop edi
pop esi
pop ebx
add esp, 216 ; 000000d8H
cmp ebp, esp
call __RTC_CheckEsp
mov esp, ebp
pop ebp
ret 0
_printf ENDP
_TEXT ENDS
My question
Why is _printf defined locally as opposed to getchar, which is defined externally?
The code for printf is right there in your listing. If you remove the assembly, you get:
; 950 : {
; 951 : int _Result;
; 952 : va_list _ArgList;
; 953 : __crt_va_start(_ArgList, _Format);
; 954 : _Result = _vfprintf_l(stdout, _Format, NULL, _ArgList);
; 955 : __crt_va_end(_ArgList);
; 956 : return _Result;
; 957 : }
So, printf is an (inline?) function that calls _vfprintf_l, which does all the heavy work (and is probably used to implement other C library functions as well).
Unfortunately, I had to re-image my laptop to install Visual Studio 2012. My project build but with above warning. Previously I had Visual Studio 2010 and I never got the above warning. The code is as follows:
__asm
{
//Initialize pointers on matrices
mov eax, dword ptr [this]
mov ebx, dword ptr [eax+UPkk]
mov dword ptr [UPkk_ptr],ebx
mov ebx, dword ptr [eax+UPk1k]
mov dword ptr [UPk1k_ptr],ebx
mov ebx, dword ptr [eax+DPk1k]
mov dword ptr [DPk1k_ptr],ebx
mov ebx, dword ptr [eax+DPkk]
mov dword ptr [DPkk_ptr],ebx
mov ebx, dword ptr [eax+mat_A]
mov dword ptr [mat_A_ptr],ebx
mov ebx, dword ptr [eax+vec_a]
mov dword ptr [vec_a_ptr],ebx
mov ebx, dword ptr [eax+vec_b]
mov dword ptr [vec_b_ptr],ebx
}
Do I need to change any settings in the project?
Best Regards
Chintan
Edit: In the above code when I replace ebx with ecx, the warnings go away and the code works fine. However, there is another piece of code where I have used ebx and ecx and in that case my program crashes. Here is the code:
__asm
{
//Initialize UPk1k[idx_4] pointer
mov eax, dword ptr [UPk1k_ptr]
mov ebx, dword ptr [idx_4]
imul ebx,8
add eax,ebx
mov dword ptr [UPk1k_id4_ptr],eax
//Initialize UPkk[idx_4] pointer
mov eax, dword ptr [UPkk_ptr]
mov ebx, dword ptr [idx_4]
imul ebx,8
add eax,ebx
mov dword ptr [UPkk_id4_ptr],eax
//Initialize UPk1k[idx_4] pointer
mov eax, dword ptr [vec_b_ptr]
mov ebx, dword ptr [idx_1]
imul ebx,8
add eax,ebx
mov dword ptr [vec_b_id1_ptr],eax
mov edi, dword ptr [idx_1] //Load idx_1 in edi
mov esi, 0 //initialize loop counter
jmp start_proc11
start_for11:inc esi //idx_2++
start_proc11:cmp esi, edi //idx_2<idx_1 ?
jge end_for11 //If yes so end of the loop
mov eax, UPk1k_id4_ptr //load UPk1k[idx_4] adress
mov ebx, vec_b_ptr //load vec_b adress
mov ecx, esi
imul ecx,8
add eax, ecx //UPk1k[idx_4+idx_2] in eax
add ebx, ecx //vec_b[idx_2] in eax
fld qword ptr [eax]//push UPk1k[idx_4+idx_2]
fld qword ptr [ebx] //push vec_b[idx_2]
mov edx,dword ptr [Sd_ptr]
fmul qword ptr [edx] //vec_b[idx_2]*Sd
fadd //pop UPk1k[idx_4+idx_2]+vec_b[idx_2]*Sd
mov edx,dword ptr [UPkk_id4_ptr]
fstp qword ptr [edx+esi*8] //pop UPkk[idx_4+idx_2]=UPk1k[idx_4+idx_2]+vec_b[idx_2]*Sd
fld qword ptr [ebx] //push vec_b[idx_2]
mov edx,dword ptr [vec_b_id1_ptr]
fld qword ptr [edx] //push vec_b[idx_2]
fmul qword ptr [eax]
fadd
fstp qword ptr [ebx]
jmp start_for11 //end of the loop
end_for11:
}
Many Thanks
Best Regards
CS
See MSDN about registers and that warning. They explain why the warning is produced: it forces the compiler to preserve value of EBX, which might be counter-productive to performance, the usual reason inline asm is used. Relevant quote:
In addition, by using EBX, ESI or EDI in inline assembly code, you
force the compiler to save and restore those registers in the function
prologue and epilogue.
To disable the warning, I think the syntax is
#pragma warning( disable : 4731 )
However, I'd try to use some other register instead, because the warning is there for a good reason, really, like most warnings.
In fact, Looking at your asm code, simply replace ebx With ecx, that should solve the problem.
test a cdecl calling convention,but it's a little confusion about this:
original C code:
int __attribute__((cdecl)) add(int a,int b)
{
int i;
i = a+b;
return i;
}
void __attribute__((cdecl)) print(int i, ...)
{
int j,a,b;
a = 2;
b = 3;
j = add(a,b);
}
void __attribute__((cdecl)) main(void)
{
print(2,3);
}
void __attribute__((cdecl)) main(void)
{
print(2,3);
}
compile code with this:
gcc test3.c -o test3 -g -mrtd
the corresponding asm like that:
(gdb) disassemble print
Dump of assembler code for function print:
0x080483cb <+0>: push ebp
0x080483cc <+1>: mov ebp,esp
0x080483ce <+3>: sub esp,0x18
0x080483d1 <+6>: mov DWORD PTR [ebp-0x8],0x2
0x080483d8 <+13>:mov DWORD PTR [ebp-0xc],0x3
0x080483df <+20>:mov eax,DWORD PTR [ebp-0xc]
0x080483e2 <+23>:mov DWORD PTR [esp+0x4],eax
0x080483e6 <+27>:mov eax,DWORD PTR [ebp-0x8]
0x080483e9 <+30>:mov DWORD PTR [esp],eax
0x080483ec <+33>:call 0x80483b4 <add>
0x080483f1 <+38>:mov DWORD PTR [ebp-0x4],eax
0x080483f4 <+41>:leave
0x080483f5 <+42>:ret
(gdb) disassemble add
Dump of assembler code for function add:
0x080483b4 <+0>: push ebp
0x080483b5 <+1>: mov ebp,esp
0x080483b7 <+3>: sub esp,0x10
0x080483ba <+6>: mov eax,DWORD PTR [ebp+0xc]
0x080483bd <+9>: mov edx,DWORD PTR [ebp+0x8]
0x080483c0 <+12>:lea eax,[edx+eax*1]
0x080483c3 <+15>:mov DWORD PTR [ebp-0x4],eax
0x080483c6 <+18>:mov eax,DWORD PTR [ebp-0x4]
0x080483c9 <+21>:leave
0x080483ca <+22>:ret
(gdb) disassemble main
Dump of assembler code for function main:
0x080483f6 <+0>: push ebp
0x080483f7 <+1>: mov ebp,esp
0x080483f9 <+3>: sub esp,0x8
0x080483fc <+6>: mov DWORD PTR [esp+0x4],0x3
0x08048404 <+14>:mov DWORD PTR [esp],0x2
0x0804840b <+21>:call 0x80483cb <print>
0x08048410 <+26>:leave
0x08048411 <+27>:ret
the cdecl convention don't add some instruction like:
add esp 8 or other similar instructions after the calling function.
why this? Thank you.
The code uses the leave instruction, which restores the stack frame. Therefore there is no need to adjust the stack pointer separately.
I have a function in VS where I pass a pointer to the function. I then want to store the pointer in a register to further manipulate. How do you do that?
I have tried
float __declspec(align(16)) x[16] =
{
0.125000, 0.125000, 0.125000, 0,
-0.125000, 0.125000, -0.125000, 0,
0.125000, -0.125000, -0.125000, 0,
-0.125000, -0.125000, 0.125000, 0
};
void e()
{
__asm mov eax, x // doesn't work
__asm mov ebx, [eax]
}
void f(float *p)
{
__asm mov eax, p // does work
__asm mov ebx, [eax]
}
int main()
{
f(x);
e();
}
Option 1 actually seems to work fine. Consider the following program:
#include <stdio.h>
void f(int *p) {
__asm mov eax, p
__asm mov ebx, [eax]
// break here
}
void main()
{
int i = 0x12345678;
f(&i);
}
With Visual Studio 2008 SP1, a single-file C++ program and debug build, I'm getting the following in the registers window when stepping into the end of f():
EAX = 004DF960
EBX = 12345678
ECX = 00000000
EDX = 00000001
ESI = 00000000
EDI = 004DF884
EIP = 003013C3
ESP = 004DF7B8
EBP = 004DF884
EFL = 00000202
Looking at the values in EAX, EBX and ESP, that looks like a pretty good evidence that you actually have the pointer you wanted in EAX. The address in EAX is just a tad higher than the one in ESP, suggesting it's one frame higher up the stack. The dereferenced value loaded into EBX suggests we got the right address.
Loading the address of a global is subtly different. The following example uses the LEA instruction to accomplish the task.
#include <stdio.h>
int a[] = { 0x1234, 0x4567 };
void main()
{
// __asm mov eax, a ; interpreted as eax <- a[0]
__asm lea eax, a ; interpreted as eax <- &a[0]
__asm mov ebx, [eax]
__asm mov ecx, [eax+4]
// break here
}
Stepping to the end of main() gets you the following register values. EAX gets the address of the first element in the array, while EBX an ECX get the values of its members.
EAX = 00157038
EBX = 00001234
ECX = 00004567
EDX = 00000001
ESI = 00000000
EDI = 0047F800
EIP = 001513C9
ESP = 0047F734
EBP = 0047F800
EFL = 00000202
The magic isn't in the LEA instruction itself. Rather, it appears that the __asm directive treats C/C++ identifiers differently depending on whether a MOV or an LEA instruction is used. Here is the ASM dump of the same program, when the MOV instruction is uncommented. Notice how the MOV instruction gets the content of a[] as its argument (DWORD PTR), while the LEA instruction gets its offset.
; ...
PUBLIC ?a##3PAHA ; a
_DATA SEGMENT
?a##3PAHA DD 01234H ; a
DD 04567H
_DATA ENDS
; ...
mov eax, DWORD PTR ?a##3PAHA
lea eax, OFFSET ?a##3PAHA
mov ebx, DWORD PTR [eax]
mov ecx, DWORD PTR [eax+4]
; ...
I'm not sure if this is correct, but have you tried casting *p to an int first and then loading that value?
void f(*p)
{
int tmp = (int)p;
// asm stuff...
}