This code has been disassembled with GDB. It is an elf executable 32 bits for x86 target. This lines come from PLT section.
0x08048320 <+0>: jmp *0x80497b0
0x08048326 <+6>: push $0x8
0x0804832b <+11>: jmp 0x8048300
I do not understand the difference between jmp * (first instruction) and the last: jmp, without asterix...
I do not understand the difference
It's the same difference as the difference between:
int x = 42;
int p = x;
and
int *px = &...;
int q = *px;
The jmp *0x80497b0 jumps to symbol whose address is stored at 0x80497b0. The jmp 0x8048300 jumps to 0x8048300.
To understand what this code does, read the "The Procedure Linkage Table" (or indeed the entire post) here.
It's same as the concept of pointers in C. If ptr is a pointer, *ptr refers to the value stored at the address pointed by ptr.
Similarly
jmp 0x8048300
means jump to the memory address 0x8048300.
And
jmp *0x80497b0
means jump to the address stored at the memory location 0x80497b0.
Related
Here is the function definition
const int& test_const_ref(const int& a) {
return a;
}
and calling it from main
int main() {
auto& x = test_const_ref(1);
printf("%d, %p\n", x, &x);
}
output as following
./debug/main
>>> 1, 0x7ffee237285c
and here is the disassembly code of test_const_ref
test_const_ref(int const&):
pushq %rbp
movq %rsp, %rbp
movq %rdi, -0x8(%rbp)
movq -0x8(%rbp), %rax
popq %rbp
retq
The question is: where does the variable x alias or where is the number 1 I passed to function test_const_ref stored ?
The code exhibits undefined behavior - the function test_const_ref returns a reference to a temporary, which lives until the end of the full-expression (the ;), and any dereference of it afterwards accesses a dangling reference.
Appearing to work is a common manifestation of UB. The program is still wrong. With optimization on, for example, Clang 12 -O2 prints: 0.
Note - there's no error in the function test_const_ref itself (apart from a design error). The UB is in main, where the dereference of the dangling int& happens during a call to printf.
Where the temporary int is stored exactly is implementation detail - but in many cases (in a Debug build, when a function isn't inlined), it would be stored on the stack:
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov dword ptr [rbp - 12], 1 # Here the 1 is stored in the stack frame
lea rdi, [rbp - 12]
call test_const_ref(int const&)
mov qword ptr [rbp - 8], rax
mov rax, qword ptr [rbp - 8]
mov esi, dword ptr [rax]
mov rdx, qword ptr [rbp - 8]
movabs rdi, offset .L.str
mov al, 0
call printf
So any subsequent use of the returned reference will access memory at [rbp - 12], that may already have been re-used for other purposes.
Note also that the compiler doesn't actually generate assembly from C++ code; it merely uses the C++ code to understand the intent, and generates another program that produces the intended output. This is known as the as-if rule. In the presence of undefined behavior, the compiler becomes free from this restriction, and may generate any output, rendering the program meaningless.
Good answers are already been given, but wrapperm explained this topic very well in here. It's going to be stored on the stack in most implementations i'm aware of.
1. The function
The language doesn't define where arguments to functions are stored. Different ABIs, for different platforms, define this.
Typically, a function argument, before any optimization, is stored on the stack. A reference is no different in this respect. What's actually stored would be a pointer to the refered-to object. Think of it this way:
const int* test_const_ref(const int* a) {
return a;
}
2. The temporary
If you were to declare a variable int foo; and call test_const_ref(foo), you know that the result would refer to foo. Since you're calling it with a temporary, all bets are off: As #fabian notes in a comment, the language only guarantees the value exist until the end of the assignment statement. Afterwards
In practice, and in your case: A compiler which allocates stack space for the temporary integer 1 will have x refer to that place, and will not use it for something else before x is defined. But if your compiler optimizes that stack allocation away - e.g. passes 1 via a register - then x has nothing to refer to and may hold junk. It might even be undefined behavior (not quite sure about that). If you're lucky, you'll get a compiler warning about it (GodBolt.org).
That's what I understood by reading some memory segmentation documents: when a function is called, there are a few instructions (called function prologue) that save the frame pointer on the stack, copy the value of the stack pointer into the base pointer and save some memory for local variables.
Here's a trivial code I am trying to debug using GDB:
void test_function(int a, int b, int c, int d) {
int flag;
char buffer[10];
flag = 31337;
buffer[0] = 'A';
}
int main() {
test_function(1, 2, 3, 4);
}
The purpose of debugging this code was to understand what happens in the stack when a function is called: so I had to examine the memory at various step of the execution of the program (before calling the function and during its execution). Although I managed to see things like the return address and the saved frame pointer by examining the base pointer, I really can't understand what I'm going to write after the disassembled code.
Disassembling:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400509 <+0>: push rbp
0x000000000040050a <+1>: mov rbp,rsp
0x000000000040050d <+4>: mov ecx,0x4
0x0000000000400512 <+9>: mov edx,0x3
0x0000000000400517 <+14>: mov esi,0x2
0x000000000040051c <+19>: mov edi,0x1
0x0000000000400521 <+24>: call 0x4004ec <test_function>
0x0000000000400526 <+29>: pop rbp
0x0000000000400527 <+30>: ret
End of assembler dump.
(gdb) disassemble test_function
Dump of assembler code for function test_function:
0x00000000004004ec <+0>: push rbp
0x00000000004004ed <+1>: mov rbp,rsp
0x00000000004004f0 <+4>: mov DWORD PTR [rbp-0x14],edi
0x00000000004004f3 <+7>: mov DWORD PTR [rbp-0x18],esi
0x00000000004004f6 <+10>: mov DWORD PTR [rbp-0x1c],edx
0x00000000004004f9 <+13>: mov DWORD PTR [rbp-0x20],ecx
0x00000000004004fc <+16>: mov DWORD PTR [rbp-0x4],0x7a69
0x0000000000400503 <+23>: mov BYTE PTR [rbp-0x10],0x41
0x0000000000400507 <+27>: pop rbp
0x0000000000400508 <+28>: ret
End of assembler dump.
I understand that "saving the frame pointer on the stack" is done by " push rbp", "copying the value of the stack pointer into the base pointer" is done by "mov rbp, rsp" but what is getting me confused is the lack of a "sub rsp $n_bytes" for "saving some memory for local variables". I've seen that in a lot of exhibits (even in some topics here on stackoverflow).
I also read that arguments should have a positive offset from the base pointer (after it's filled with the stack pointer value), since if they are located in the caller function and the stack grows toward lower addresses it makes perfect sense that when the base pointer is updated with the stack pointer value the compiler goes back in the stack by adding some positive numbers. But my code seems to store them in a negative offset, just like local variables.. I also can't understand why they are put in those registers (in the main).. shouldn't they be saved directly in the rsp "offsetted"?
Maybe these differences are due to the fact that I'm using a 64 bit system, but my researches didn't lead me to anything that would explain what I am facing.
The System V ABI for x86-64 specifies a red zone of 128 bytes below %rsp. These 128 bytes belong to the function as long as it doesn't call any other function (it is a leaf function).
Signal handlers (and functions called by a debugger) need to respect the red zone, since they are effectively involuntary function calls. All of the local variables of your test_function, which is a leaf function, fit into the red zone, thus no adjustment of %rsp is needed. (Also, the function has no visible side-effects and would be optimized out on any reasonable optimization setting).
You can compile with -mno-red-zone to stop the compiler from using space below the stack pointer. Kernel code has to do this because hardware interrupts don't implement a red-zone.
But my code seems to store them in a negative offset, just like local variables
The first x86_64 arguments are passed on registers, not on the stack. So when rbp is set to rsp, they are not on the stack, and cannot be on a positive offset.
They are being pushed only to:
save register state for a second function call.
In this case, this is not required since it is a leaf function.
make register allocation easier.
But an optimized allocator could do a better job without memory spill here.
The situation would be different if you had:
x86_64 function with lots of arguments. Those that don't fit on registers go on the stack.
IA-32, where every argument goes on the stack.
the lack of a "sub rsp $n_bytes" for "saving some memory for local variables".
The missing sub rsp on red zone of leaf function part of the question had already been asked at: Why does the x86-64 GCC function prologue allocate less stack than the local variables?
I've looked at assembler output for the following piece of code and I was stunned:
int x=0, y=0; // global
// r1, r2 are ints, local.
std::thread t([&x, &y, &r1, &r2](){
x = 1;
r1 = y;
});
!std::thread t([&x, &y, &r1, &r2](){
<lambda()>::operator()(void) const+0: push %rbp
<lambda()>::operator()(void) const+1: mov %rsp,%rbp
<lambda()>::operator()(void) const+4: mov %rdi,-0x8(%rbp)
<lambda()>::operator()(void) const+18: mov -0x8(%rbp),%rax
<lambda()>::operator()(void) const+22: mov (%rax),%rax
! x = 1;
<lambda()>::operator()(void) const()
<lambda()>::operator()(void) const+8: movl $0x1,0x205362(%rip) # 0x6062ac <x>
! r1 = y;
<lambda()>::operator()(void) const+25: mov 0x205359(%rip),%edx # 0x6062b0 <y>
<lambda()>::operator()(void) const+31: mov %edx,(%rax)
!
!});
<lambda()>::operator()(void) const+33: nop
<lambda()>::operator()(void) const+34: pop %rbp
<lambda()>::operator()(void) const+35: retq
Why address of x,y is determined relate to RIP. RIP is a instruction pointer so it seems to be wild. Especially, I have never seen something like that. ( Perhaps I haven't seen a lot of stuff :)).
The only explanation that comes to my head is the fact, that lambda is a closure and taking environment variables from particular place has something in common with RIP.
Code doesn't move at run-time, once the code section is loaded the routine are not copied or moved around.
Static data also occupy the same address once their section is loaded.
So the distance between an instruction and a static variable is known at compile time and it is invariant under relocation of the module base (as both the instruction and the data are translated by the same amount).
So RIP-relative addressing not only is not wild, but it has always been a long time missing feature.
While in 32-bit code an instruction like mov eax, [var] is innocuous, in 64-bit without RIP-relative addressing it requires 9 bytes, 1 for the opcode and 8 for the immediate.
With RIP-relative addressing the immediates are still 32 bits.
C++ lamdbas are a syntactic sugar for a function object, where the captured variables become instance variables.
Variables captured by reference are handled as pointer/reference.
Global variables don't need any special treatment when captured as they are already accessible.
You rightfully noted that x and y are accessed respectively as 0x205362(%rip) and 0x205359(%rip).
Since they are global their address is fixed at runtime and RIP-relative addressing is used to access them.
However you forgot to check how r1, a local captured variable, is accessed.
It is stored with a (%rax) and rax was previously loaded as (optimizing) movq (%rdi), %rax.
%rdi is the first parameter of the method operator(), so it is this, the instruction just mentioned loads the first instance variable into rax and then use that value to access r1.
Simply put it is a pointer (or better a reference) to r1, since r1 lives on the stack its address is dynamic at run-time (it depends on the state of the stack).
So lambda use both indirect and RIP-relative addressing, thereby contradicting the hypothesis that RIP-relative addressing was somehow special.
Note that the capturing mechanism doesn't extend the life of capture variables (like in ECMAScript), so capturing a local var by reference in a lambda for std::thread is nearly always a bad idea.
I am newbie in ASM and C/C++. Many site show me a picture that not only the Pointers are "point to" somewhere (address) they store; but also the Junm. Please someone tell me know "what is the main difference between ASM JUMP instruction and the Pointer in C/C++". Thanks guys.
Pointers are used to store the address of the variables.
ASM JUMP is used by processor to start executing code from the address.
I dont think that there is any relevant reason to differentiate between the two as they both are different concepts and are used for different reasons.
They are not similar, pointers are just addresses, they may seem mystical in c/c++ due to the abstractions but in assembly a pointer could be a register with an address stored in it that "points" to some data, EIP is a pointer that "points" to the current instruction during the programs execution, the whole idea is that it's "cheaper" to copy an address that points to data than the data itself.
I think the confusion comes from the fact that c/c++ are strongly type languages and assembly is loosely typed.
This wiki article on Strong and weak typing explains it all, it has this to say about pointers..
Pointers
Some programming languages expose pointers as if they were numeric values, and allow users to perform arithmetic on them. These languages are sometimes referred to as "weakly typed", since pointer arithmetic can be used to bypass the language's type system.
Even with this said if you read this tutorial on pointers it says;
a[5] = 0; // a [offset of 5] = 0
*(a+5) = 0; // pointed by (a+5) = 0
these two expressions are equivalent which makes sense when you think about it.
a is just a pointer for an array in assembly you might have something roughly like this;
.data
a db "some data"
.data is a pointer to the address where the data lives
a: is also a pointer to the address that begins right where the label a is in the program just before definition of the byte 's' in "some data" just like if the pointer a in c was;
char a[] = "some data"; // or
char *a = "some data"; // and a is the start address
accessing them looks like;
a[5] == 'd' && *(a+5) == 'd'; // true
pointing to a looks like;
char *b = a;
access looks like this in assembly;
mov al, byte[a+5] // calculates effective address or points to the 'd'
cmp al, 'd' // if al == 'd'
je some_appropriate_label // je(jump if equal) somewhere anywhere
//(some_appropriate_label being an address or pointer to the begining of some appropriate code)
pointing to or getting the address in assembly looks something like;
mov ebx, a // moves the address that a represents into ebx
mov bl, byte[ebx+5] // moves 'd' into bl
In assembly everything is pretty exposed.
I felt like doing some extra investigation for you, i made this simple c program called test.c;
int main(){
char *pointer1 = "some data";
char *pointer2 = pointer1;
}
and fed gcc -S -masm=intel -fno-asynchronous-unwind-tables -fno-dwarf2-cfi-asm test.c through gcc to get test.s the assembly equivalent of test.c this is the file;
.file "test.c"
.intel_syntax noprefix
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "some data\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
push ebp
mov ebp, esp
and esp, -16
sub esp, 16
call ___main
mov DWORD PTR [esp+12], OFFSET FLAT:LC0
mov eax, DWORD PTR [esp+12]
mov DWORD PTR [esp+8], eax
leave
ret
.ident "GCC: (rev2, Built by MinGW-builds project) 4.8.1"
notice these parts;
LC0:
.ascii "some data\0"
and
call ___main
mov DWORD PTR [esp+12], OFFSET FLAT:LC0
mov eax, DWORD PTR [esp+12]
mov DWORD PTR [esp+8], eax
It looks like this program is using the stack to store it's pointers, esp is the stack pointer, it contains the address or pointer to the top of the stack at any time.
pointer1
mov DWORD PTR [esp+12], OFFSET FLAT:LC0 // moves address where data lives into stack
// this is where pointer1 lives
pointer2
mov eax, DWORD PTR [esp+12] // moves address/pointer into eax from stack
mov DWORD PTR [esp+8], eax // moves address/pointer into pointer2
// esp+12 is the c pointer (think *(a+0) a[0] from c but instead of char 's' it's an address(dword),
// LCO is the data that was moved into the pointer which is also an address
// The second line is basically saying;
// move the 4byte address to the topOfStack+8bytes
I'm having trouble understanding the behavior of the MS VC compiler on this one. This line compiles fine, but the result I get is not what I'd expect at all:
this->Test((char *)&CS2 - (char *)&CS1 == sizeof(void *));
The CS1 and CS2 arguments are declared as follows:
myFunction(tCS1* CS1, tCS2* CS2) {...
tCS1 and tCS2 are structures containing one int and one __int64, resp.
This is meant to check the distance on the stack between my arguments CS1 and CS2, which are both pointers. When I break execution on this line and use the debugger to get the addresses of my two variables, I find that they indeed are 8 bytes away from each other (x64 platform).
However, the result of the comparison is false.
Here is the assembly code generated by the compiler:
mov rax,qword ptr [CS1]
mov rdi,qword ptr [CS2]
sub rdi,rax
(then it does the comparison using the result stored in rdi, and makes the call)
Yes, the compiler is comparing the values of my pointer arguments, rather than their addresses. I'm missing a level of indirection here, where did it go?
Of course I can't reproduce this in a test environment, and I have no clue where to look anymore.
I'm cross-compiling this bit of code on a 32-bits machine to an x64 platform (I have to), that's the only 'odd' thing about it. Any idea, any hint?
The assembly
mov rax,qword ptr [CS1]
mov rdi,qword ptr [CS2]
sub rdi,rax
indicates CS1 and CS2 are not really stack arguments, but rather some global symbols - if I wanted to produce similar results, I'd do something like this:
int* CS1 = NULL, *CS2 = NULL; /* or any other value...*/
#define CS1 *CS1
#define CS2 *CS2
Of course this is ugly code - but have you checked you haven't such things in your code? Also, dynamic linker might play a role in it.
And last but not least: If you attempt to write code like:
void foo()
{
int a;
int b;
printf("%d", &a-&b);
}
You should be aware that this is actually undefined behaviour, as C (and C++) only permits to subtract pointers pointing inside a single object (eg. array).
As #jpalacek and commenters observed this is undefined and the compiler may be taking advantage of it to do whatever it likes. It is pretty strange.
This code "works" on gcc:
#include
int func(int *a, int *b)
{
return (char *)&a - (char *) &b;
}
int main(void)
{
int a, b;
printf("%d", func(&a, &b));
return 0;
}
(gdb) disassemble func
Dump of assembler code for function func:
0x0 80483e4 : push %ebp
0x080483e5 : mov %esp,%ebp
=> 0x080483e7 : lea 0x8(%ebp),%edx
0x080483ea : lea 0xc(%ebp),%eax
0x080483ed : mov %edx,%ecx
0x080483ef : sub %eax,%ecx
0x080483f1 : mov %ecx,%eax
0x080483f3 : pop %ebp
0x080483f4 : ret
End of assembler dump.
and with optimization it just knows their relative addresses:
(edit: answer was truncated here for some reason)
(gdb) disassemble func
Dump of assembler code for function func:
0x08048410 : push %ebp
0x08048411 : mov $0xfffffffc,%eax
0x08048416 : mov %esp,%ebp
0x08048418 : pop %ebp
0x08048419 : ret
End of assembler dump.
The interesting thing is that with -O4 optimization it returns +4 and without it, it returns -4.
Why are you trying to do this anyhow? There's no guarantee in general that the arguments have any memory address: they may be passed in registers.