Building lea instructions int * q = p ++ and int c = a ++

To deepen the impression of how "(* p) ++" works, I wrote some test codes, for example:

int main() { int a = 3; int *p = &a; int b = (*p)++; int *q = p++; int c = a++; int d = c++; printf("a = %d, b = %d, c = %d, d = %d, p = %#x, q = %#x\n",a, b, c, d, p, q); } 

OUTPUT IS: a = 5, b = 3, c = 5, d = 4, p = 0xc6dc3490, q = 0xc6dc348c

But my question is about assembly (codes are in order, not off):

 main: push rbp mov rbp, rsp sub rsp, 48 ;int a = 3 : mov DWORD PTR [rbp-36], 3 ;int *p = &a : lea rax, [rbp-36] mov QWORD PTR [rbp-8], rax ;int b = (*p)++ : mov rax, QWORD PTR [rbp-8] mov eax, DWORD PTR [rax] lea ecx, [rax+1] ;Flag1 mov rdx, QWORD PTR [rbp-8] mov DWORD PTR [rdx], ecx mov DWORD PTR [rbp-12], eax ;int *q = p++ : mov rax, QWORD PTR [rbp-8] ;Flag2 lea rdx, [rax+4] ;Flag3 mov QWORD PTR [rbp-8], rdx mov QWORD PTR [rbp-24], rax ;int c = a++; mov eax, DWORD PTR [rbp-36] lea edx, [rax+1] ;Flag4 mov DWORD PTR [rbp-36], edx mov DWORD PTR [rbp-28], eax ;int d = c++; mov eax, DWORD PTR [rbp-28] lea edx, [rax+1] ;Flag5 mov DWORD PTR [rbp-28], edx mov DWORD PTR [rbp-32], eax ... ... (ignore some) 

Please pay attention to the lines of "Flagx", which confuse me.
From above we know that
when the pointer: int * q = p ++ :

 lea rdx, [rax+4] ;Flag3 

Here, lea seems to read the store of addr values ​​in rax and +4. then go to "rdx".

while: int c = a ++ or int d = C ++ :

 lea edx, [rax+1] ;Flag4/Flag5 

Here, β€œlea” seems to read the contents of the addr value store in β€œrax” (here 3), and +1 to 4 and goes to β€œedx”.

But! the point is that rax 'in the two statutes is the same . They are all from

 mov rax, QWORD PTR [rbp-8] ;Flag2 

As we can see, they (Flag3 and Flag4 / Flag5) look very similar, but they work differently on the same "rax", how did it happen? Can the lea command distinguish between rdx and edx / ecx and get different results?
Thank you very much.

+5
source share
3 answers

Here, β€œlea” seems to read the contents of the addr value store in β€œrax” (here 3) and +1, comes to 4 and goes to β€œedx”.

No, you are mistaken. lea edx, [rax+1] does not change rax . rax already 3 before lea .

But! The fact is that the "rax" in these two statutes is the same. All of them are from mov rax, QWORD PTR [rbp-8]

No, you are mistaken. rax installed mov eax, DWORD PTR [rbp-36] .

Different parts of general registers can be referenced using different names.

  64 32 16 8 0 | | | | | vvvvv +----+----+----+----+----+----+----+----+ | | | | | | | | | +----+----+----+----+----+----+----+----+ |<------------------------------------->| rax |<----------------->| eax |<------->| ax |<-->| ah |<-->| al 

This means that when you write on eax , you also write the lower half of rax (and the upper half is nullified).

So,

  ; rax eax rdx edx ; q = p++ ; +----+----+----+----+ +----+----+----+----+ A1 mov rax, QWORD PTR [rbp-8] ; | p | | ??? | A2 lea rdx, [rax+4] ; | p | | p+4 | A3 mov QWORD PTR [rbp-8], rdx ; | p | | p+4 | A4 mov QWORD PTR [rbp-24], rax ; | p | | p+4 | ; c = a++ ; | p | | p+4 | B1 mov eax, DWORD PTR [rbp-40] ; | 0 | a | | p+4 | B2 lea edx, [rax+1] ; | 0 | a | | 0 | a+1 | B3 mov DWORD PTR [rbp-40], edx ; | 0 | a | | 0 | a+1 | B4 mov DWORD PTR [rbp-28], eax ; | 0 | a | | 0 | a+1 | ; +----+----+----+----+ +----+----+----+----+ 
+5
source

p and q are pointers to int , and the size of int is 4 on your platform. Thus, the increment p actually increases its value by 4 .

 int *q = p++; mov rax, QWORD PTR [rbp-8] ; rax = p lea rdx, [rax+4] ; same as rdx = rax + 4 mov QWORD PTR [rbp-8], rdx ; p = rdx mov QWORD PTR [rbp-24], rax ; q = rax 

c is int . Thus, the increment a actually increases its value by 1 .

 c = a++; mov eax, DWORD PTR [rbp-40] ; rax = a (yes modifying eax actually modifies rax) lea edx, [rax+1] ; same as edx = rax + 1 mov DWORD PTR [rbp-40], edx ; a = edx mov DWORD PTR [rbp-28], eax ; c = eax (eax still contains the inital value of a) 

Read more about LEA instructions here .

+1
source

In the line int *q = p++ the address pointer is incremented. As you know, the size of int is 4 bytes, and the size of int is the size of the pointer variable, so in the assembly code you can see lea rdx, [rax+4] .
But in the string int c = a++ value of the variable a increases. Therefore, in the build code, you can see lea edx, [rax+1] .

Note. The size of an int may vary from compiler to compiler. But according to GCC compilers GCC and in your case, int has a length of 4 bytes

-1
source

Source: https://habr.com/ru/post/1269243/


All Articles