It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
Say you do:
void something()
{
int* number = new int(16);
int* sixteen = number;
}
How does the CPU know the address that I want to assign to sixteen?
Thanks
There's no magic in your example code. Take this snippet, for example:
int x = 5;
int y = x;
Your code with pointers is exactly the same - the computer doesn't need to know any magic information, it's just copying whatever's in number into sixteen.
As to your comment below:
but how does it know where x or y are in memory. If I ask to copy x into y, how does it know where either of those are.
In practice, on most machines these days, probably neither of them will be in memory, they'll be in registers. But if they are in memory, then yes, the compiler will emit code that keeps track of all of those addresses as necessary. In this case, they'd be on the stack, so the machine code would be accessing the stack pointer register and dereferencing it with some compiler-decided offsets that refer to the storage of each particular variable.
Here's an example. This simple function:
int f(void)
{
int x = 5;
int y = x;
return y;
}
When compiled with clang and no optimizations, gives me the following output on my machine:
_f:
pushq %rbp ; save caller's base pointer
movq %rsp,%rbp ; copy stack pointer into base pointer
movl $5,0xfc(%rbp) ; store constant 5 to stack at rbp-4
movl 0xfc(%rbp),%eax ; copy value at rbp-4 to register eax
movl %eax,0xf8(%rbp) ; copy value from eax to stack at rbp-8
movl 0xf8(%rbp),%eax ; copy value off stack to return value register eax
popq %rbp ; restore caller's base pointer
ret ; return from function
I added some comments to explain what each line of the generated code does. The important things to see are that there are two variables on the stack - one at 0xf8(%rbp) (or rbp-8 to be clearer) and one at 0xfc(%rbp) (or rbp-4). The basic algorithm is just like the original code shows - the constant 5 gets saved into x at rbp-4, then that value gets copied over into y at rbp-8.
"But where does the stack come from?" you might ask. The answer to that question is operating system and compiler dependent, though. It's all set up prior to your program's main function being called, at the same time as other runtime setup required by your operating system takes place.
The CPU knows because your program tells it. The magic here is in the compiler. First I build this program in Visual Studio 2010.
This is the disassembly that it generates (in DEBUG mode):
void something()
{
003A13C0 push ebp
003A13C1 mov ebp,esp
003A13C3 sub esp,0E8h
003A13C9 push ebx
003A13CA push esi
003A13CB push edi
003A13CC lea edi,[ebp-0E8h]
003A13D2 mov ecx,3Ah
003A13D7 mov eax,0CCCCCCCCh
003A13DC rep stos dword ptr es:[edi]
int* number = new int(16);
003A13DE push 4
003A13E0 call operator new (3A1186h)
After the call to operator new, EAX = 00097C58 which is the address that the memory manager decided to give me this run of the program. This is the address that will be used whenever you dereference number.
003A13E5 add esp,4
003A13E8 mov dword ptr [ebp-0E0h],eax
003A13EE cmp dword ptr [ebp-0E0h],0
003A13F5 je something+51h (3A1411h)
003A13F7 mov eax,dword ptr [ebp-0E0h]
003A13FD mov dword ptr [eax],10h
003A1403 mov ecx,dword ptr [ebp-0E0h]
003A1409 mov dword ptr [ebp-0E8h],ecx
003A140F jmp something+5Bh (3A141Bh)
003A1411 mov dword ptr [ebp-0E8h],0
003A141B mov edx,dword ptr [ebp-0E8h]
003A1421 mov dword ptr [number],edx
int* sixteen = number;
003A1424 mov eax,dword ptr [number]
003A1427 mov dword ptr [sixteen],eax
Here you're just making sure that sixteen is the same value as number. So now they point at the same address.
}
You can verify by inspecting them in the Locals debug window:
+ number 0x00097c58 int *
+ sixteen 0x00097c58 int *
You can do this experiment and step through the disassembly. It is often very enlightening.
Related
Hi heads up this is a homework. I'm given an assembly generated by MSVC 32-bit Release with optimizations on, and I'm supposed to decode it back into C++. I've included the top of the function to the line I'm having problems with. The comments are mine, which I'm wrote while trying to understand this.
Note: Code is supposedly generated from C++. Not traditional ASM.
Note 2: There is one area of undefined behavior in the code.
Here are the lines I'm stuck with
TheFunction: ; TheFunction(int* a, int s);
0F2D4670 push ebp ; Push/clear/save ebp
0F2D4671 mov ebp,esp ; ebp now points to top of stack
0F2D4673 push ecx ; Push/clear/save ecx
0F2D4674 push ebx ; Push/clear/save ebs
0F2D4675 push esi ; Push/clear/save esi
0F2D4676 mov ebx,edx ; ebx = int s
0F2D4678 mov esi,1 ; esi = 1
0F2D467D push edi ; calling convention ; Push/clear/save edi
0F2D467E mov edi,dword ptr [a (0F2D95E8h)] ; edi = a[0]
0F2D4684 cmp ebx,esi ; if(s < 1)
0F2D4686 jl SomeFunction+3Ch (0F2D46ACh) ; Jump to return
0F2D4688 nop dword ptr [eax+eax] ; !! <-- No op involving dereferencing? What does this do?
0F2D4690 mov eax,dword ptr [edi+esi*4-4] ; !! <-- edi is *a, while esi is 1. There is no address
here!
..... More code but I've figured these out ....
I've more or less got the gist of the function. Its a function that takes a pointer to an int, with an underlying array, and a size. It then goes through each element in the array from last to first, adding to each subsequent one and printing it out. However, I still haven't got the details down and need help
Two questions, both at the end of the code snippet. What does no op on a dereference pointer do, and am I reading the last line in that its attempting to dereference something not in memory?
The nop dword ptr [eax+eax] instruciton does nothing. It doesn't even access the memory location given by the operand. It literally performs no operation.
It's just there so the next instruction is aligned to a 16-byte boundary. You'll notice that next instruction address is 0F2D4690 which ends with 0 which means it's 16-byte aligned. This can improve the performance of loops. Somewhere there will be an instruction that jumps back to 0F2D4690 as part of a loop. This particular form of a NOP instruction is used because it encodes a single NOP instruction in 8 bytes.
There is no corresponding C++ code for this instruction. You shouldn't try to represent it in your C++ code, just ignore it.
Also note that your comment for mov edi,dword ptr [a (0F2D95E8h)] is incorrect. Instead of being edi = a[0] it's simply edi = a. The variable a isn't a parameter at all, instead it's a global (or file level static) variable located at memory location 0F2D95E8h. This instruction just loads the value from memory.
Let's assume I pass a new object to a function like this:
loadContainer->addControlView( new BmpView( BMP_PICTURE ) );
Now, I want to change a specific characteristic of the BmpView before I pass it to addControlView. The way I do this is like this:
Control* newView = new BmpView( BMP_PICTURE );
newView->changeColor( WHITE );
loadContainer->addControlView( newView );
Does this create an extra temporary/local object? Or is there an equal amount of memory allocated in both cases?
The only added memory allocated in your function is a new pointer *newView, which its size is pretty low and doesn't affect by the actual size of the BmpView. It doesn't allocate twice memory for BmpView.
I'm not considering any memory overhead of calling changeColor, which I assume wasn't the point of this question.
In both cases, there is single call to new, hence the amount of memory used is equal. (Note, this is without speculating about any allocation possibly requested by BmpView constructor, changeColor(), etc.)
However, you may wish to refactor your code to ensure some exception safety, avoid potential leaks hence ensure the amount of memory used is under control:
// C++11
std::unique_ptr<Control> newView(new BmpView(BMP_PICTURE));
// C++14, preferred
//auto newView = std::make_unique<BmpView>(BMP_PICTURE);
newView->changeColor( WHITE );
loadContainer->addControlView( newView.release() );
Reference for the below code/assembly :
https://godbolt.org/g/8DgmC1
#include <cstdio>
class ValueClass {
public:
// Class content not important...
int someValue;
};
void PrintValueClass(ValueClass* ptr) {
printf("%d\n", ptr->someValue);
}
int main() {
PrintValueClass(new ValueClass());
ValueClass* pValueClass = new ValueClass();
pValueClass->someValue = 55;
PrintValueClass(pValueClass);
return 1;
}
Compiled Assembly (PrintValueClass redacted as not important to question at hand) :
Example where you pass the (new ValueClass) directly to the function.
main:
push rbp
mov rbp, rsp
mov edi, 4
call operator new(unsigned long)
mov DWORD PTR [rax], 0
mov rdi, rax
call PrintValueClass(ValueClass*)
mov eax, 1
pop rbp
ret
Example where you create a local variable holding the pointer, do something to it, and then pass it to the function.
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov edi, 4
call operator new(unsigned long)
mov DWORD PTR [rax], 0
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
mov DWORD PTR [rax], 55
mov rax, QWORD PTR [rbp-8]
mov rdi, rax
call PrintValueClass(ValueClass*)
mov eax, 1
leave
ret
Before diving into the assembly, if your question is does the new operation occur twice if you store the pointer in a variable first, the answer is no. As shown through the assembly, the new 'function' is only called once, so only sizeof(ValueClass) is ever being allocated here through some sort of heap allocation function. But I feel the need to answer the question fully, even if it was not exactly intended to ask this question. Is extra memory used? Technically yes, realistically no.
The only difference between these two pieces of code is stack 'allocation', noted by the sub rsp, 16, which essentially means 'allocate' 16 bytes on the stack for local variables. So truly the only difference here is 16 bytes, which will greatly change on what compiler you use, what architecture you target, and many more factors.
At the end of the day, I would go as far to say, you would never care about the extra 16 bytes.
I'm studying assembly language from the book "Assembly Language Step-by-Step: Programming with Linux" by Jeff Dunteman, and have come across an interesting paragraph in the book which I'm most likely misunderstanding, hence would appreciate some clarification on:
"The stack must be clean before we destroy the stack frame and return control. This simply means that any temporary values that we may have pushed onto the stack during the program’s run must be gone. All that is left on the stack should be the caller’s EBP, EBX, ESI, and EDI values.
...
Once the stack is clean, to destroy the stack frame we must first pop the caller’s register values back into their registers, ensuring that the pops are in the correct order.
...
We restore the caller’s ESP by moving the value from EBP into ESP, and finally pop the caller’s EBP value off the stack."
Consider the following code generated from Visual Studio 2008:
int myFuncSum( int a, int b)
{
001B1020 push ebp
001B1021 mov ebp,esp
001B1023 push ecx <------------------
int c;
c = a + b;
001B1024 mov eax,dword ptr [ebp+8]
001B1027 add eax,dword ptr [ebp+0Ch]
001B102A mov dword ptr [ebp-4],eax
return c;
001B102D mov eax,dword ptr [ebp-4]
}
001B1030 mov esp,ebp
001B1032 pop ebp
001B1033 ret
The value of ecx (indicated), pushed to make space on the stack for my variable c, is, as far as I can see, only gone from the stack when we reset ESP; however, as quoted, the book states that the stack must be clean before we reset ESP. Can someone please clarify whether or not I am missing something?
The example from Visual Studio 2008 doesn't contradict the book. The book is covering the most elaborate case of a call. See the x86-32 Calling Convention as a cross-reference which spells it out with pictures.
In your example, there were no caller registers saved on the stack, so there are no pop instructions to be performed. This is part of the "clean up" that must occur before mov esp, ebp that the book is referring to. So more specifically, let's suppose the callee is saving si and di for the caller, then the prelude and postlude for the function might look like this:
push ebp ; save base pointer
mov ebp, esp ; setup stack frame in base pointer
sub esp, 4 ; reserve 4 bytes of local data
push si ; save caller's registers
push di
; do some stuff, reference 32-bit local variable -4(%ebp), etc
; Use si and di for our own purposes...
; clean up
pop di ; do the stack clean up
pop si ; restoring the caller's values
mov esp, ebp ; restore the stack pointer
pop ebp
ret
In your simple example, there were no saved caller registers, so no final pop instructions needed at the end.
Perhaps because it's simpler or faster, the compiler elected to do the following instruction in place of sub esp, 4:
push ecx
But the effect is the same: reserve 4 bytes for a local variable.
Notice the instruction:
push ebp
mov ebp,esp ; <<<<=== saves the stack base pointer
and the instruction:
mov esp,ebp ; <<<<<== restore the stack base pointer
pop ebp
So after this sequence the stack is clean again
I noticed that the constructor will move this to eax before returning. This is a return value or something else?
class CTest {
int val_;
public:
CTest() {
0093F700 push ebp
0093F701 mov ebp,esp
0093F703 sub esp,0CCh
0093F709 push ebx
0093F70A push esi
0093F70B push edi
0093F70C push ecx
0093F70D lea edi,[ebp-0CCh]
0093F713 mov ecx,33h
0093F718 mov eax,0CCCCCCCCh
0093F71D rep stos dword ptr es:[edi]
0093F71F pop ecx
0093F720 mov dword ptr [this],ecx
val_ = 1;
0093F723 mov eax,dword ptr [this]
0093F726 mov dword ptr [eax],1
}
0093F72C mov eax,dword ptr [this]
0093F72F pop edi
0093F730 pop esi
0093F731 pop ebx
0093F732 mov esp,ebp
0093F734 pop ebp
0093F735 ret
VS2012 debug mode
I found that new will use its "return value". Seems like if(operator new() == 0) return 0; else return constructor();
class CTest {
int val_;
public:
CTest() {
val_ = 1;
__asm {
mov eax, 0x12345678
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
}
}
};
int main() {
CTest *test = new CTest; // test == 0x12345678
return 0;
}
Your second question disagrees with your first. How can new use if ( operator new() == 0 ) return 0; else return constructor(); if constructor() is producing the condition result?
Anyway…
What the compiler does with registers is the compiler's business. Registers tend to hold whatever information is immediately useful, and if the compiler is written with the belief that every time the constructor is used, the object is used immediately afterwards, it may reasonably choose to put this in a register.
An ABI may require constructors to do this, but I doubt any do. Anyway, such protocols only apply to things exported from libraries, not strictly within programs.
Any new expression does check the result of operator new against 0 before proceeding to initialize an object. operator new may signal failure by returning nullptr (or NULL, etc.).
This can actually be a problem with placement new expressions, because it represents unavoidable runtime overhead as the given pointer is generally already known to be non-null.
This can be a feature by design, in C++ and other languages, returning a reference to a given instance allows a more "idiomatic" use of the features offered by the object itself, in short it's the Named parameter Idiom .
But this is just 1 option, it can be useful sometimes, especially if you are able to design your library in a way that it only "takes actions" without having the need to pass a significant amount of parameters, so the chain of method calls stays readable.
I got the following simple C++ code:
#include <stdio.h>
int main(void)
{
::printf("\nHello,debugger!\n");
}
And from WinDbg, I got the following disassembly code:
SimpleDemo!main:
01111380 55 push ebp
01111381 8bec mov ebp,esp
01111383 81ecc0000000 sub esp,0C0h
01111389 53 push ebx
0111138a 56 push esi
0111138b 57 push edi
0111138c 8dbd40ffffff lea edi,[ebp-0C0h]
01111392 b930000000 mov ecx,30h
01111397 b8cccccccc mov eax,0CCCCCCCCh
0111139c f3ab rep stos dword ptr es:[edi]
0111139e 8bf4 mov esi,esp
011113a0 683c571101 push offset SimpleDemo!`string' (0111573c)
011113a5 ff15b0821101 call dword ptr [SimpleDemo!_imp__printf (011182b0)]
011113ab 83c404 add esp,4
011113ae 3bf4 cmp esi,esp
011113b0 e877fdffff call SimpleDemo!ILT+295(__RTC_CheckEsp) (0111112c)
011113b5 33c0 xor eax,eax
011113b7 5f pop edi
011113b8 5e pop esi
011113b9 5b pop ebx
011113ba 81c4c0000000 add esp,0C0h
011113c0 3bec cmp ebp,esp
011113c2 e865fdffff call SimpleDemo!ILT+295(__RTC_CheckEsp) (0111112c)
011113c7 8be5 mov esp,ebp
011113c9 5d pop ebp
011113ca c3 ret
I have some difficulties to fully understand it. What is the SimpleDemo!ILT things doing here?
What's the point of the instruction comparing ebp and esp at 011113c0?
Since I don't have any local variables in main() function, why there's still a sub esp,0C0h at the loacation of 01111383?
Many thanks.
Update 1
Though I still don't know what ILT means, but the __RTC_CheckESP is for runtime checks. These code can be elimiated by placing the following pragma before the main() function.
#pragma runtime_checks( "su", off )
Reference:
http://msdn.microsoft.com/en-us/library/8wtf2dfz.aspx
http://msdn.microsoft.com/en-us/library/6kasb93x.aspx
Update 2
The sub esp,0C0h instruction allocate another 0C0h bytes extra space on the stack. Then EAX is filled with 0xCCCCCCCC, this is 4 bytes, since ECX=30h, 4*30h=0C0h, so the instruction rep stos dword ptr es:[edi] fill exactly the extra spaces with 0xCC. But what is this extra space on stack for? Is this some kind of safe belt? Also I notice that if I turn off the runtime check as Update 1 shows, there's still such extra space on stack, though much smaller. And this space is not filled with 0xCC.
The assembly code without runtime check is like below:
SimpleDemo!main:
00231250 55 push ebp
00231251 8bec mov ebp,esp
00231253 83ec40 sub esp,40h <-- Still extra space allocated from stack, but smaller
00231256 53 push ebx
00231257 56 push esi
00231258 57 push edi
00231259 683c472300 push offset SimpleDemo!`string' (0023473c)
0023125e ff1538722300 call dword ptr [SimpleDemo!_imp__printf (00237238)]
00231264 83c404 add esp,4
00231267 33c0 xor eax,eax
00231269 5f pop edi
0023126a 5e pop esi
0023126b 5b pop ebx
0023126c 8be5 mov esp,ebp
0023126e 5d pop ebp
0023126f c3 ret
Most of the instructions are part of MSVC runtime checking, enabled by default for debug builds. Just calling printf and returning 0 in an optimized build takes much less code. (Godbolt compiler explorer). Other compilers (like GCC and clang) don't do as much stuff like stack-pointer comparison after calls, or poisoning stack memory with a recognizable 0xCC pattern to detect use-uninitialized, so their debug builds are like MSVC debug mode without its extra runtime checks.
I've annotated the assembler, hopefully that will help you a bit. Lines starting 'd' are debug code lines, lines starting 'r' are run time check code lines. I've also put in what I think a debug with no runtime checks version and release version would look like.
; The ebp register is used to access local variables that are stored on the stack,
; this is known as a stack frame. Before we start doing anything, we need to save
; the stack frame of the calling function so it can be restored when we finish.
push ebp
; These two instructions create our stack frame, in this case, 192 bytes
; This space, although not used in this case, is useful for edit-and-continue. If you
; break the program and add code which requires a local variable, the space is
; available for it. This is much simpler than trying to relocate stack variables,
; especially if you have pointers to stack variables.
mov ebp,esp
d sub esp,0C0h
; C/C++ functions shouldn't alter these three registers in 32-bit calling conventions,
; so save them. These are stored below our stack frame (the stack moves down in memory)
r push ebx
r push esi
r push edi
; This puts the address of the stack frame bottom (lowest address) into edi...
d lea edi,[ebp-0C0h]
; ...and then fill the stack frame with the uninitialised data value (ecx = number of
; dwords, eax = value to store)
d mov ecx,30h
d mov eax,0CCCCCCCCh
d rep stos dword ptr es:[edi]
; Stack checking code: the stack pointer is stored in esi
r mov esi,esp
; This is the first parameter to printf. Parameters are pushed onto the stack
; in reverse order (i.e. last parameter pushed first) before calling the function.
push offset SimpleDemo!`string'
; This is the call to printf. Note the call is indirect, the target address is
; specified in the memory address SimpleDemo!_imp__printf, which is filled in when
; the executable is loaded into RAM.
call dword ptr [SimpleDemo!_imp__printf]
; In C/C++, the caller is responsible for removing the parameters. This is because
; the caller is the only code that knows how many parameters were put on the stack
; (thanks to the '...' parameter type)
add esp,4
; More stack checking code - this sets the zero flag if the stack pointer is pointing
; where we expect it to be pointing.
r cmp esi,esp
; ILT - Import Lookup Table? This is a statically linked function which throws an
; exception/error if the zero flag is cleared (i.e. the stack pointer is pointing
; somewhere unexpected)
r call SimpleDemo!ILT+295(__RTC_CheckEsp))
; The return value is stored in eax by convention
xor eax,eax
; Restore the values we shouldn't have altered
r pop edi
r pop esi
r pop ebx
; Destroy the stack frame
r add esp,0C0h
; More stack checking code - this sets the zero flag if the stack pointer is pointing
; where we expect it to be pointing.
r cmp ebp,esp
; see above
r call SimpleDemo!ILT+295(__RTC_CheckEsp)
; This is the usual way to destroy the stack frame, but here it's not really necessary
; since ebp==esp
mov esp,ebp
; Restore the caller's stack frame
pop ebp
; And exit
ret
; Debug only, no runtime checks
push ebp
mov ebp,esp
d sub esp,0C0h
d lea edi,[ebp-0C0h]
d mov ecx,30h
d mov eax,0CCCCCCCCh
d rep stos dword ptr es:[edi]
push offset SimpleDemo!`string'
call dword ptr [SimpleDemo!_imp__printf]
add esp,4
xor eax,eax
mov esp,ebp
pop ebp
ret
; Release mode (The optimiser is clever enough to drop the frame pointer setup with no VLAs or other complications)
push offset SimpleDemo!`string'
call dword ptr [SimpleDemo!_imp__printf]
add esp,4
xor eax,eax
ret
Number one your code's main() is improperly formed. It doesn't return the int you promised it would return. Correcting this defect, we get:
#include
int main(int argc, char *argv[])
{
::printf("\nHello,debugger!\n");
return 0;
}
Additionally, any more, it is very strange to see #include <stdio.h> in a C++ program. I believe you want #include <cstdio>
In all cases, space must be made on the stack for arguments and for return values. main()'s return value requires stack space. main()s context to be saved during the call to printf() requires stack space. printf()'s arguments require stack space. printf()'s return value requires stack space. That's what the 0c0h byte stack frame is doing.
The first thing that happens is the incoming bas pointer is copied to the top of the stack. Then the new stack pointer is copied into the base pointer. We'll be checking later to be sure that the stack winds up back where it started from (because you have runtime checking turned on). Then we build the (0C0h bytes long) stack frame to hold our context and printf()'s arguments during the call to printf(). We jump to printf(). When we get back, we hop over the return value which you didn't check in your code (the only thing left on its frame) and make sure the stack after the call is in the same place it was before the call. We pop our context back off the stack. We then check that the final stack pointer matches the value we saved way up at the front. Then we pop the prior value of the base pointer off the very top of the stack and return.
That is code that is inserted by the compiler when you build with runtime checking (/RTC). Disable those options and it should be clearer. /GZ could also be causing this depending on your VS version.
For the record, I suspect that ILT means "Incremental Linking Thunk".
The way incremental linking (and Edit&Continue) works is the following: the linker adds a layer of indirection for every call via thunks which are grouped at the beginning of executable, and adds a huge reserved space after them. This way, when you're relinking the updated executable it can just put any new/changed code into the reserved area and patch only the affected thunks, without changing the rest of the code.
The 40 bytes is the worst case stack allocation for any called or subsequently called function. This is explained in glorious detail here.
What is this space reserved on the top of the stack for? First, space is created for any local variables. In this case, FunctionWith6Params() has two. However, those two local variables only account for 0x10 bytes. What’s the deal with the rest of the space created on the top of the stack?
On the x64 platform, when code prepares the stack for calling another function, it does not use push instructions to put the parameters on the stack as is commonly the case in x86 code. Instead, the stack pointer typically remains fixed for a particular function. The compiler looks at all of the functions the code in the current function calls, it finds the one with the maximum number of parameters, and then creates enough space on the stack to accommodate those parameters. In this example, FunctionWith6Params() calls printf() passing it 8 parameters. Since that is the called function with the maximum number of parameters, the compiler creates 8 slots on the stack. The top four slots on the stack will then be the home space used by any functions FunctionWith6Params() calls.