I try to understand [c code -> assembly] code
void node::Check( data & _data1, vector<data2*>& _data2)
{
-> push ebp
-> mov ebp,esp
-> push ebx
if (TryToCheck(_data1.a, _data1.b))
-> mov ebx,dword ptr [_data1]
I'm not sure what this line does
What is the meaning of this line,
push esi
mov esi,ecx
ecx is a value for counting as I know .
Why save the value of ecx in esi?
mov eax,dword ptr [esi+50h]
What does this mean the value, 50h?
mov eax,dword ptr [eax+4]
lea edx,[ebx+40h]
lea ecx,[esi+50h]
push edx
lea edx,[ebx+50h]
push edx
call eax
test al,al
je node::Check+7Fh
push ebp
mov ebp,esp
This saves the caller's stackframe pointer (push ebp) and sets up your stackframe pointer (mov ebp,esp).
push ebx
This saves ebx on the stack. Somewhere later, before the return, you will see a pop ebx.
mov ebx,dword ptr [_data1]
This moves the content of location _data1 into the ebx register. The debugger shows you a symbolic name; if you would look more closely you will see move ebx,dword ptr [ebp+12]. Here the parameters of the caller are accessed using your stackframe pointer in ebp.
push esi
mov esi,ecx
This saves esi and then moves the value of ecx into esi.
mov eax,dword ptr [esi+50h]
This moves the value of the memory location at esi+50h into eax.
Sometimes you musn't worry too much about the assembler the compiler generates. It maybe sub-optimal or may be so optimized that you can't recognize your original statements.
Related
Here is a basic program I written on the godbolt compiler, and it's as simple as:
#include<stdio.h>
void main()
{
int a = 10;
int *p = &a;
printf("%d", *p);
}
The results after compilation I get:
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-12], 10
lea rax, [rbp-12]
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
mov eax, DWORD PTR [rax]
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
nop
leave
ret
Question: Pushing the rbp, making the stack frame by making a 16 byte block, how from a register, a value is moved to a stack location and vice versa, how the job of LEA is to figure out the address, I got this part.
Problem:
lea rax, [rbp-12]
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
mov eax, DWORD PTR [rax]
Lea -> getting address of rbp-12 into rax,
then moving the value which is the address of rbp-12 into rax,
but next line again says, move to rax, the value of rbp-8. This seems ambiguous. Then again moving the value of rax to eax. I don't understand the amount of work here. Why couldn't I have done
lea rax, [rbp-12]
mov QWORD PTR [rbp-8], rax
mov eax, QWORD PTR [rbp-8]
and be done with it? coz on the original line, rbp-12's address is stored onto rax, then rax stored to rbp-8. then rbp-8 stored again into rax, and then again rax is stored into eax? couldn't we have just copied the rbp-8 directly to eax? i guess not. But my question is why?
I know there is de-referencing in pointers, so How LEA helps grabbing the address of rbp-12, I understand, but on the next parts, when did it went from grabbing values from addresses I completely lost. And also, after that, I didn't understand any of the asm lines.
You're seeing very un-optimized code. Here's my line-by-line interpretation:
.LC0:
.string "%d" ; Format string for printf
main:
push rbp ; Save original base pointer
mov rbp, rsp ; Set base pointer to beginning of stack frame
sub rsp, 16 ; Allocate space for stack frame
mov DWORD PTR [rbp-12], 10 ; Initialize variable 'a'
lea rax, [rbp-12] ; Load effective address of 'a'
mov QWORD PTR [rbp-8], rax ; Store address of 'a' in 'p'
mov rax, QWORD PTR [rbp-8] ; Load 'p' into rax (even though it's already there - heh!)
mov eax, DWORD PTR [rax] ; Load 32-bit value of '*p' into eax
mov esi, eax ; Load value to print into esi
mov edi, OFFSET FLAT:.LC0 ; Load format string address into edi
mov eax, 0 ; Zero out eax (not sure why -- likely printf call protocol)
call printf ; Make the printf call
nop ; No-op (not sure why)
leave ; Remove the stack frame
ret ; Return
Compilers, when not optimizing, generate code like this as they parse the code you gave them. It's doing a lot of unnecessary stuff, but it is quicker to generate and makes using a debugger easier.
Compare this with the optimized code (-O2):
.LC0:
.string "%d" ; Format string for printf
main:
mov esi, 10 ; Don't need those variables -- just a 10 to pass to printf!
mov edi, OFFSET FLAT:.LC0 ; Load format string address into edi
xor eax, eax ; It's a few cycles faster to xor a register with itself than to load an immediate 0
jmp printf ; Just jmp to printf -- it will handle the return
The optimizer found that the variables weren't necessary, so no stack frame is created. Nothing is left but the printf call! And that's done as a jmp since nothing else need be done here when the printf is complete.
I am trying to write a nasm program that can move and print dynamic arrays using malloc and printf. The program sometimes crashes and sometimes works when it tries to print an array. When I edit the code so that it doesn't print anything, the program always works. The method "movad" sets a dynamic array equal to another array and "setad" sets an array to a static array that was declared at the beginning of the program. The array variable stores a pointer to a malloced piece of memory that stores the array. The first dword of the array is the length and the rest is the contents.
Here is the code:
%include "io.inc"
CEXTERN malloc
section .data
fmt: db "%c%c %c%c",0
start: dd 2
dd 'a'
dd 'b'
section .bss
array1: resd 1
array2: resd 1
a: resd 1
section .text
global _main
_main:
push ebp
mov ebp,esp
push dword start
push dword array1
call setad
add esp,8
push dword array1
push dword array2
call movad
add esp,8
mov ebx,[array2]
push dword [ebx+8]
push dword [ebx+4]
mov ebx,[array1]
push dword [ebx+8]
push dword [ebx+4]
push dword fmt
call printf
add esp,20
mov esp,ebp
pop ebp
ret
movad:
push ebp
mov ebp,esp
push ebx
push ecx
push edi
push esi
mov edi,[ebp+8]
mov esi,[ebp+12]
mov ebx,[esi]
push dword [ebx]
add dword [esp],4
call malloc
add esp,4
mov [edi],eax
mov ecx,[esi]
mov ecx,[ecx]
add ecx,1
mov edi,[edi]
mov esi,[esi]
cld
rep movsd
pop esi
pop edi
pop ecx
pop ebx
mov esp,ebp
pop ebp
ret
setad:
push ebp
mov ebp,esp
push eax
lea eax,[ebp+12]
push eax
push dword [ebp+8]
call movad
add esp,8
pop eax
mov esp,ebp
pop ebp
ret
How can I make it so that it never crashes when it prints something
I'm doing a program in assembly to read a disk through ports (0x1f0-0x1f7) and I'm mixing it with c. I have a function in assembly that I will call in my c main funtion. My main function as 1 parameter: sectors to read:
Kernel.c
extern int _readd(int nmrsector);
(...)
int sector = 257;
int error = _readd(sector);
if(error == 0) PrintString("Error"); //It is declared on my screen.h file
disk.asm
global _readd
_readd:
push eax
push ebx
push ecx
push edx
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov ecx, eax
cmp ecx, 256
jg short _fail
jne short _good
_fail:
xor eax, eax
leave
ret
_good:
xor eax, eax
mov eax, 12
leave
ret
It crashes when run it with VirtualBox. Any ideas?
If you save CPU registers when you enter a function, you need to restore them when you are finished. Your PUSHs need to be matched with POPs.
Also, if you use a stack frame to access local variables and parameters, setup the frame (push ebp ; mov ebp, esp) before everything, so you can more easily refer to them. Here [ebp+8] doesn't refer to a parameter, because you alter the stack before setting up the frame.
I've written this procedure in asm:
.586
.model flat, stdcall
.xmm
.data
.code
EncryptAsm proc plainText:ptr byte, heigth:DWORD, inputLength:DWORD, encryptedText:ptr byte, cipherArray:ptr byte
local addRow:WORD
local row:DWORD
local column:DWORD
local iterator:DWORD
local forLoopIteratorI:DWORD
local forLoopIteratorJ:DWORD
push esi
push edi
push ebx
push ecx
push edx
mov addRow,0
mov row,0
mov column,0
mov iterator,0
mov forLoopIteratorI,0
mov forLoopIteratorJ,0
mov ecx,heigth
FILL_CIPHER_ARRAY_LOOP: mov eax, inputLength
cmp iterator,eax
jge PREPARE_ITERATOR
push ecx ;pushing heigth value
mov ecx,row ;calculating index of cipher array index=[row*inputLength+column]
imul ecx,inputLength
add ecx,column
mov eax,iterator
mov edx,plainText
mov al,[edx+eax]
mov [esi],al
mov ebx, cipherArray
mov [ebx+ecx],al
movsb
pop ecx;getting back heigth value
add column,1
cmp addRow,0
je INC_ROW
cmp addRow,0
jne DEC_ROW
INC_ROW: add row,1
jmp ROW_COMPARE
DEC_ROW: sub row,1
jmp ROW_COMPARE
ROW_COMPARE: cmp row,ecx
jge IF_STATEMENT_1
cmp row,0
jl IF_STATEMENT_2
jmp INCREMENT_ITERATOR
IF_STATEMENT_1: sub row,2
mov addRow,1
jmp INCREMENT_ITERATOR
IF_STATEMENT_2: add row,2
mov addRow,0
jmp INCREMENT_ITERATOR
INCREMENT_ITERATOR: add iterator,1
jmp FILL_CIPHER_ARRAY_LOOP
PREPARE_ITERATOR: mov iterator,0
READ_CIPHER_ARRY_LOOP_I:cmp forLoopIteratorI,ecx
jge PREPARE_ITERATOR_2
READ_CIPHER_ARRY_LOOP_J:mov eax, inputLength
cmp forLoopIteratorJ,eax
jge PREPARE_I_AND_J
push ecx ;pushing heigth value
mov ecx,forLoopIteratorI ;calculating index of cipher array
imul ecx,inputLength
add ecx,forLoopIteratorJ
mov ebx,cipherArray
mov al,[ebx+ecx]
cmp al,'#'
jne COPY_VALUE
ITERATE: add forLoopIteratorJ,1
pop ecx
jmp READ_CIPHER_ARRY_LOOP_J
PREPARE_I_AND_J: mov forLoopIteratorJ,0
add forLoopIteratorI,1
jmp READ_CIPHER_ARRY_LOOP_I
COPY_VALUE: push edi
mov edi,iterator
mov edx,encryptedText
mov [edx+edi],al
add iterator,1
pop edi
jmp ITERATE
PREPARE_ITERATOR_2: mov iterator,0
FINISH: mov eax, encryptedText
pop edx
pop ecx
pop ebx
pop edi
pop esi
ret
EncryptAsm endp
end
It implements rail fence cipher algorithm (at the end the variable encryptedText contains ciphered plainText). It works fine, I mean it does the encryption well but after all I'm getting memory corrupt error... I'm calling this procedure as an extern one from C app. I can print the encrypted text without any problem but when returning 0 in main function, the memory corrupt error pops up.
I don't know what can cause it. At the beginning of asm procedure, I push all the registers' values and pop them after the whole operation.
The error message:
Unhandled exception at 0x72676F74 in ConsoleApplication12.exe: 0xC0000005: Access violation executing location 0x72676F74.
I'll be greatful for any hint.
One possible candidate could be here:
mov edx,plainText
mov al,[edx+eax]
mov [esi],al
esi is pushed, from the caller, but where is it initialized? It seems that it uses whatever is there from the caller. Same for edi so where will movsb store it?
UPDATE
Since I don't know your algorithm and don't see how it is used I can only guess. BUt I think you should do the following before the loop:
mov esi, plainText
mov edi, encryptedText
mov ebx, cipherArray
Since you don't change these values you can then change this code:
COPY_VALUE: push edi
mov edi,iterator
mov edx,encryptedText
mov [edx+edi],al
add iterator,1
pop edi
jmp ITERATE
to this:
COPY_VALUE: mov edx,iterator
mov [edx+edi],al
inc iterator
jmp ITERATE
Generally instead of using add x, 1 you can use inc x which is shorter.
People I've found it! The cause of the error was... 'movsb'. I was using it in a previous version of my algorithm and I've forgotten to delete it... The rest of my code works fine. Thanks for all the answers and for willing to help m ;)
I need to understand HOW longjmp function works; I know what it does, but I need to know how it does it.
I tried to disas the code in gdb but I can't understand some steps. The code is:
0xb7ead420 <siglongjmp+0>: push %ebp
0xb7ead421 <siglongjmp+1>: mov %esp,%ebp
0xb7ead423 <siglongjmp+3>: sub $0x18,%esp
0xb7ead426 <siglongjmp+6>: mov %ebx,-0xc(%ebp)
0xb7ead429 <siglongjmp+9>: call 0xb7e9828f <_Unwind_Find_FDE#plt+119>
0xb7ead42e <siglongjmp+14>: add $0x12bbc6,%ebx
0xb7ead434 <siglongjmp+20>: mov %esi,-0x8(%ebp)
0xb7ead437 <siglongjmp+23>: mov 0xc(%ebp),%esi
0xb7ead43a <siglongjmp+26>: mov %edi,-0x4(%ebp)
0xb7ead43d <siglongjmp+29>: mov 0x8(%ebp),%edi
0xb7ead440 <siglongjmp+32>: mov %esi,0x4(%esp)
0xb7ead444 <siglongjmp+36>: mov %edi,(%esp)
0xb7ead447 <siglongjmp+39>: call 0xb7ead4d0
0xb7ead44c <siglongjmp+44>: mov 0x18(%edi),%eax
0xb7ead44f <siglongjmp+47>: test %eax,%eax
0xb7ead451 <siglongjmp+49>: jne 0xb7ead470 <siglongjmp+80>
0xb7ead453 <siglongjmp+51>: test %esi,%esi
0xb7ead455 <siglongjmp+53>: mov $0x1,%eax
0xb7ead45a <siglongjmp+58>: cmove %eax,%esi
0xb7ead45d <siglongjmp+61>: mov %esi,0x4(%esp)
0xb7ead461 <siglongjmp+65>: mov %edi,(%esp)
0xb7ead464 <siglongjmp+68>: call 0xb7ead490
0xb7ead469 <siglongjmp+73>: lea 0x0(%esi,%eiz,1),%esi
0xb7ead470 <siglongjmp+80>: lea 0x1c(%edi),%eax
0xb7ead473 <siglongjmp+83>: movl $0x0,0x8(%esp)
0xb7ead47b <siglongjmp+91>: mov %eax,0x4(%esp)
0xb7ead47f <siglongjmp+95>: movl $0x2,(%esp)
0xb7ead486 <siglongjmp+102>: call 0xb7ead890 <sigprocmask>
0xb7ead48b <siglongjmp+107>: jmp 0xb7ead453 <siglongjmp+51>
Can someone briefly explain me the code, or indicate where I can find the source code in the system?
Here is the i386 code for longjmp, in the standard i386 ABI, without any crazy extensions for interaction with C++, exceptions, cleanup functions, signal mask, etc.:
mov 4(%esp),%edx
mov 8(%esp),%eax
test %eax,%eax
jnz 1f
inc %eax
1:
mov (%edx),%ebx
mov 4(%edx),%esi
mov 8(%edx),%edi
mov 12(%edx),%ebp
mov 16(%edx),%ecx
mov %ecx,%esp
mov 20(%edx),%ecx
jmp *%ecx
Mostly, it restores the registers and stack as they were at the time of the corresponding setjmp(). There is some additional cleanup required (fixing signal handling and unwinding pending stack handlers), as well as returning a different value as the apparent return value of setjmp, but restoring the state is the essence of the operation.
For it to work, the stack cannot be below the point at which setjmp was called. Longjmp is a brutish way to just forget everything that has been called below it down to the same level in the call stack (or function call nesting sequence) mostly by simply setting the stack pointer to the same frame it was when setjmp was called.
For it to work cleanly, longjmp() calls all the exit handlers for intermediate functions, so they can delete variables, and whatever other cleanup is normally done when a function returns. Resetting the stack to a point less deep releases all the auto variables but if one of those is a FILE *, the file needs to be closed and the i/o buffer freed too.
I think you need to see Procedure Activation Records and Call Stacks and Setjmp.h 's jmp_buf's structure.
Quoted from Expert C Programming: Deep C Secrets:
Setjmp saves a copy of the program counter and the current pointer to the top of the stack. This saves some initial values, if you like. Then longjmp restores these values effectively transferring control and resetting the state back to where you were when you did the save. It's termed "unwinding the stack", because you unroll activation records from the stack until you get to the saved one.
Have a look at page 153 also here.
The stackframe will be highly dependent on the machine and the executable, but the idea is the same.
In Windows X64 MASM
.code
my_jmp_buf STRUCT
_Frame QWORD ?;
_Rbx QWORD ?;
_Rsp QWORD ?;
_Rbp QWORD ?;
_Rsi QWORD ?;
_Rdi QWORD ?;
_R12 QWORD ?;
_R13 QWORD ?;
_R14 QWORD ?;
_R15 QWORD ?;
_Rip QWORD ?;
_MxCsr DWORD ?;
_FpCsr WORD ?;
_Spare WORD ?;
_Xmm6 XMMWORD ?;
_Xmm7 XMMWORD ?;
_Xmm8 XMMWORD ?;
_Xmm9 XMMWORD ?;
_Xmm10 XMMWORD ?;
_Xmm11 XMMWORD ?;
_Xmm12 XMMWORD ?;
_Xmm13 XMMWORD ?;
_Xmm14 XMMWORD ?;
_Xmm15 XMMWORD ?;
my_jmp_buf ENDS
;extern "C" int my_setjmp(jmp_buf env);
public my_setjmp
my_setjmp PROC
mov rax, [rsp] ;save ip
mov (my_jmp_buf ptr[rcx])._Rip, rax
lea rax, [rsp + 8] ;save sp before call this function
mov (my_jmp_buf ptr[rcx])._Rsp, rax
mov (my_jmp_buf ptr[rcx])._Frame, rax
;save gprs
mov (my_jmp_buf ptr[rcx])._Rbx,rbx
mov (my_jmp_buf ptr[rcx])._Rbp,rbp
mov (my_jmp_buf ptr[rcx])._Rsi,rsi
mov (my_jmp_buf ptr[rcx])._Rdi,rdi
mov (my_jmp_buf ptr[rcx])._R12,r12
mov (my_jmp_buf ptr[rcx])._R13,r13
mov (my_jmp_buf ptr[rcx])._R14,r14
mov (my_jmp_buf ptr[rcx])._R15,r15
;save fp and xmm
stmxcsr (my_jmp_buf ptr[rcx])._MxCsr
fnstcw (my_jmp_buf ptr[rcx])._FpCsr
movdqa (my_jmp_buf ptr[rcx])._Xmm6,xmm6
movdqa (my_jmp_buf ptr[rcx])._Xmm7,xmm7
movdqa (my_jmp_buf ptr[rcx])._Xmm8,xmm8
movdqa (my_jmp_buf ptr[rcx])._Xmm9,xmm9
movdqa (my_jmp_buf ptr[rcx])._Xmm10,xmm10
movdqa (my_jmp_buf ptr[rcx])._Xmm11,xmm11
movdqa (my_jmp_buf ptr[rcx])._Xmm12,xmm12
movdqa (my_jmp_buf ptr[rcx])._Xmm13,xmm13
movdqa (my_jmp_buf ptr[rcx])._Xmm14,xmm14
movdqa (my_jmp_buf ptr[rcx])._Xmm15,xmm15
xor rax,rax
ret
my_setjmp ENDP
;extern "C" void my_longjmp(jmp_buf env, int value);
public my_longjmp
my_longjmp PROC
;restore fp and xmm
movdqa xmm15,(my_jmp_buf ptr[rcx])._Xmm15
movdqa xmm14,(my_jmp_buf ptr[rcx])._Xmm14
movdqa xmm13,(my_jmp_buf ptr[rcx])._Xmm13
movdqa xmm12,(my_jmp_buf ptr[rcx])._Xmm12
movdqa xmm11,(my_jmp_buf ptr[rcx])._Xmm11
movdqa xmm10,(my_jmp_buf ptr[rcx])._Xmm10
movdqa xmm9,(my_jmp_buf ptr[rcx])._Xmm9
movdqa xmm8,(my_jmp_buf ptr[rcx])._Xmm8
movdqa xmm7,(my_jmp_buf ptr[rcx])._Xmm7
movdqa xmm6,(my_jmp_buf ptr[rcx])._Xmm6
fldcw (my_jmp_buf ptr[rcx])._FpCsr
ldmxcsr (my_jmp_buf ptr[rcx])._MxCsr
;restore gprs
mov r15, (my_jmp_buf ptr[rcx])._R15
mov r14, (my_jmp_buf ptr[rcx])._R14
mov r13, (my_jmp_buf ptr[rcx])._R13
mov r12, (my_jmp_buf ptr[rcx])._R12
mov rdi, (my_jmp_buf ptr[rcx])._Rdi
mov rsi, (my_jmp_buf ptr[rcx])._Rsi
mov rbp, (my_jmp_buf ptr[rcx])._Rbp
mov rbx, (my_jmp_buf ptr[rcx])._Rbx
;retore sp
mov rsp, (my_jmp_buf ptr[rcx])._Rsp
;restore ip
mov rcx, (my_jmp_buf ptr[rcx])._Rip; must be the last instruction as rcx modified
;return value
mov rax, rdx
jmp rcx
my_longjmp ENDP
END
You pass setjmp() a buffer parameter. It then stores the current register info etc. into this buffer. The call to longjmp() then restores these values from the buffer. Furthermore, what wallyk said.
Here are versions of setmp and longjmp I wrote for a small clib subset (written and tested in Visual Studio 2008). The assembly code is stored in a separate .asm file.
.586
.MODEL FLAT, C ; Flat memory model, C calling conventions.
;.STACK ; Not required for this example.
;.DATA ; Not required for this example.
.code
; Simple version of setjmp (x86-32 bit).
;
; Saves ebp, ebx, edi, esi, esp and eip in that order.
;
setjmp_t proc
push ebp
mov ebp, esp
push edi
mov edi, [ebp+8] ; Pointer to jmpbuf struct.
mov eax, [ebp] ; Save ebp, note we are saving the stored version on the stack.
mov [edi], eax
mov [edi+4], ebx ; Save ebx
mov eax, [ebp-4]
mov [edi+8], eax ; Save edi, note we are saving the stored verion on the stack.
mov [edi+12], esi ; Save esi
mov eax, ebp;
add eax, 8
mov [edi+16], eax ; Save sp, note saving sp pointing to last item on stack just before call to setjmp.
mov eax, [ebp+4]
mov [edi+20], eax ; Save return address (will be used as jump address in longjmp().
xor eax, eax ; return 0;
pop edi
pop ebp
ret
setjmp_t endp
; Simple version of longjmp (x86-32 bit).
;
; Restores ebp, ebx, edi, esi, esp and eip.
;
longjmp_t proc
mov edi, [esp+4] ; Pointer to jmpbuf struct.
mov eax, [esp+8] ; Get return value (value passed to longjmp).
mov ebp, [edi] ; Restore ebp.
mov ebx, [edi+4] ; Restore ebx.
mov esi, [edi+12] ; Restore esi.
mov esp, [edi+16] ; Restore stack pointer.
mov ecx, [edi+20] ; Original return address to setjmp.
mov edi, [edi+8] ; Restore edi, note, done last as we were using edi up to this point.
jmp ecx ; Wing and a prayer...
longjmp_t endp
end
A C code snippet to test it:
extern "C" int setjmp_t( int *p);
extern "C" int longjmp_t( int *p, int n);
jmp_buf test2_buff;
void DoTest2()
{
int x;
x = setjmp_t( test2_buff);
printf( "setjmp_t return - %d\n", x);
switch (x)
{
case 0:
printf( "About to do long jump...\n");
longjmp_t( test2_buff, 99);
break;
default:
printf( "Here becauuse of long jump...\n");
break;
}
printf( "Test2 passed!\n");
}
Note that I used the declaration from 'setjmp.h' for the buffer but if you want you can use an array of ints (minimum of 6 ints).