A quick note on the LEA instruction of x86 family.

[ Check out all posts in “low-level” series here. ]

In my previous post on low-level code, we deconstructed a few assembly patterns. There were a few lea instructions in the example assembly. I figured I can share a few links on why lea is interesting.

Here is one occurrance of it:

...
lea   rax, [var_11h]
mov   rdi, rax
call  sym.Foo::RandChar
...

For this discussion, let’s get a more raw disassembly of that part.

Below command is for binutils version of objdump (this_is_rdi being the executable):

objdump --disassemble=main -C -M intel this_is_rdi

This is the relevant part of the output:

...
lea    rax,[rbp-0x9]
mov    rdi,rax
call   11fc <Foo::RandChar()>
...

You can check out the definition of lea here. Basically, it stores the “effective address” computed in a register, rather than accessing the data at the calculated address.

We already discussed that rbp stores a pointer to the base of the stack frame. What this lea did is to get the address stored in rbp, subtract 0x9 from that, and write the result in rax. So rax now stores the memory address of some data on stack.1

This kind of address calculation can be called the main function of lea. However, an address is a number, so lea can also be used for some generic, combined arithmetic operations. And that use does often show up in optimized generated code.

Take this example:

lea    r8d,[rcx+rcx*2]

Or same instruction, in att syntax:

leal	(%rcx,%rcx,2), %r8d

As it should be clear from the intel syntax, the instruction is used to “multiply the value stored in rcx by 3”, and write the result to r8d. In this case, the value that was originally stored in rcx was not an address at all. You can find:

Some more details on the subtleties around lea instruction is available in the “16.1 LEA instruction (all processors)” section of Agner Fog’s manual called: “Optimizing subroutines in assembly language: An optimization guide for x86 platforms”. The manual can be found in this page. It states:

The LEA instruction is useful for many purposes because it can do a shift operation, two additions, and a move in just one instruction.

That’s all for today. If you find technical errors, please report in the blog’s Issues page.

Thanks for reading!

  1. To remind what we already explained in a previous post: The mov will copy that address to rdi. The copy in rdi is “the this pointer” being passed as an implicit argument to the call to Foo::RandChar