Memory Layout
Typical Memory Distribution of the next process in Linux:
________________ 0 xffffffff </P> <p> kernel space </P> <p> ________________ 0xc0000000 </P> <p> stack </P> <p> (extended down) </P> <p> ________________ </P> <p> unused </P> <p> ____________________ </P> <p> dynamic libraries </P> <p> ________________ 0x40000000 </P> <p> unused </P> <p> ________________ </P> <p> heap </P> <p> (expanded upwards) </P> <p> ________________ </P> <p> read/write sections </P> <p> (. data ,. BSS) </P> <p> ____________________ </P> <p> readonly sections </P> <p> (. init ,. rodata ,. text) </P> <p> ________________ 0x0804800 </P> <p> reserved </P> <p> ________________ 0 <br/>
Functions and stacks
Stack stores the maintenance information required for a function call, which is often called a stack frame or activity record.
Save the following content:
1. Return address and parameters of the Function
2. Temporary variables: including non-static local variables of the function and other temporary variables automatically generated together.
3. Saved context: Includes registers that need to be kept unchanged before and after function calls.
A common activity record is as follows:
_____________ </P> <p> parameter </P> <p> _____________ </P> <p> return address </P> <p> _____________ </P> <p> old EBP <-EBP </P> <p> _____________ </P> <p> saved registers </P> <p> _____________ </P> <p> Local variable </P> <p> _____________ </P> <p> other data </P> <p> _____________ <-ESP <br/>
The i386 function body starts with "standard:
1. Push EBP: Push EBP into Stack
2. mov EBP, esp: EBP = ESP (in this case, EBP points to the top of the stack, and the top of the stack is the old EBP)
3. [Optional] sub ESP, XXX: allocate XXX bytes of temporary space on the stack
4. [Optional] Push XXX: Save the registers named XXX if necessary.
The end of the "standard" is as follows:
1. [Optional] Pop XXX: restores the register if necessary.
2. mov ESP and EBP: recover ESP and reclaim local variable space
3. Pop: Restore the saved EBP value from the stack.
4. RET: Get the return address from the stack and jump to this location
Disassemble a function:
Int Foo ()
{
Return 123;
}
As follows:
004113a0 push EBP step one, save EBP, and point EBP to the top of the current stack <br/> 004113a1 mov EBP, esp </P> <p> 004113a3 sub ESP, 0c0h step two, open up a space on the stack </P> <p> 004113a9 push EBX step three, save EBX, ESI, EDI register <br/> 004113aa push ESI <br/> 004113ab push EDI </P> <p> 004113ac Lea EDI, [ebp-0C0h] Step Four, add debugging information <br/> 004113b2 mov ECx, 30 h <br/> 004113b7 mov eax, 0 ccccccch <br/> 004113bc rep STOs dword ptr es [di] </P> <p> 004113be mov eax, 7bh step five, 123 is returned (the returned value is transmitted through the eax register) <br/> 004113c3 pop EDI Step six, stack recovery register <br/> 004113c4 pop ESI <br/> 004113c5 pop EBX </P> <p> 004113c6 mov ESP, EBP step seven, recover the ESP, EBP <br/> 004113c8 pop EBP </P> <p> 004113c9 RET step eight before entering the function, and use the RET command to return <br/>
Supplement:
Function call:
Call 0x0ea23h
Apply the ECS or EIP to the stack;
Transfer to 0x0ea23h.
Returned results of the RET function:
RET
Obtain the EIP or ECs from the stack;
Return the instruction indicated by the EIP to continue execution.
Function return value Transmission Method
# Include <stdio. h> <br/> typedef struct big_thing <br/>{< br/> char Buf [128]; <br/>} big_thing; </P> <p> big_thing return_rest () <br/> {<br/> big_thing B; <br/> B. buf [0] = 0; <br/> return B; <br/>}< br/> int main () <br/>{< br/> big_thing n = return_rest (); <br/> return 0; <br/>}
The pseudocode is as follows:
Void return_test (void * temp) <br/>{< br/> big_thing B; <br/> B. buf [0] = 0; <br/> memcpy (temp, & B, sizeof (big_thing); <br/> eax = temp; <br/>}</P> <p> int main () <br/> {<br/> big_thing temp; <br/> big_thing N; <br/> return_test (& temp); <br/> memcpy (& N, eax, sizeof (big_thing); <br/>}
The procedure is as follows:
1. First, the main function opens up an additional space on the stack, and part of the space is used as a temporary object for passing the returned value, temp.
2. Pass the temp object address as a hidden parameter to the return_test function.
3. The return_test function copies the data to the temp object and transmits the address of the temp object to eax.
4. After return_test is returned, the main function copies the content of the temp object pointed to by eax to n.
When the C ++ object returns,
# Include <iostream> <br/> using namespace STD; </P> <p> struct cpp_obj <br/>{< br/> cpp_obj () <br/>{< br/> cout <"ctor/N"; <br/>}</P> <p> cpp_obj (const cpp_obj & C) <br/>{< br/> cout <"Copy ctor/N "; <br/>}</P> <p> cpp_obj & operator = (const cpp_obj & RHs) <br/>{< br/> cout <"operator =/N"; <br/> return * This; <br/>}</P> <p> ~ Cpp_obj () <br/>{< br/> cout <"dtor/N"; <br/>}< br/> }; </P> <p> cpp_obj return_test () <br/> {<br/> cpp_obj B; <br/> cout <"before return/N "; <br/> return B; <br/>}</P> <p> int main () <br/>{< br/> cpp_obj N; <br/> N = return_test (); <br/> return 0; <br/>}
Result:
Ctor <br/> ctor <br/> before return <br/> copy ctor <br/> dtor <br/> operator = <br/> dtor
# Include <iostream> <br/> using namespace STD; </P> <p> struct cpp_obj <br/>{< br/> cpp_obj () <br/>{< br/> cout <"ctor/N"; <br/>}</P> <p> cpp_obj (const cpp_obj & C) <br/>{< br/> cout <"Copy ctor/N "; <br/>}</P> <p> cpp_obj & operator = (const cpp_obj & RHs) <br/>{< br/> cout <"operator =/N"; <br/> return * This; <br/>}</P> <p> ~ Cpp_obj () <br/>{< br/> cout <"dtor/N"; <br/>}< br/> }; </P> <p> cpp_obj return_test () <br/> {<br/> cout <"before return/N"; <br/> return cpp_obj (); <br/>}</P> <p> int main () <br/> {<br/> cpp_obj N; <br/> N = return_test (); <br/> return 0; <br/>}
When the function is changed to this type, C ++ optimizes the return value and constructs the object directly on the temporary object used for outgoing access. The result is as follows:
Ctor <br/> before return <br/> ctor <br/> operator = <br/> dtor
Heap
In Windows, the virtual space obtained by using virtualalloc is similar to the "wholesale" space to the operating system.
The space allocated by heapalloc or malloc is actually the "retail" of heap space ".
This is because if the memory management is performed by the operating system kernel, the performance overhead of the system calling is high, and the program manages the memory more efficiently.
Heap allocation algorithms include:
1. Idle linked list
2. Header/subject/idle bitmap
3. Object pool
In the actual system, multiple methods are used to allocate heap space.