LEA is extremely useful on x86-64 for a different reason: it lets you determine your program counter.
For example, imagine you have code like this:
static int foo;
int *func(void) { return &foo; }
If you build it as position-independent code, on x86-64, you get:
leaq foo(%rip), %rax
ret
Here, "foo" turns into the offset from the end of the LEA instruction to the variable foo. The x86 address calculation engine does the rest.
On x86-32, there's no IP-relative addressing, so you can't use LEA like this, and you get a mess, which I've trimmed into readability:
; x86-32 has no instruction that reads EIP. Fake it.
__x86.get_pc_thunk.ax:
movl (%esp), %eax
ret
func:
call __x86.get_pc_thunk.ax ; <-- this sucks
addl $_GLOBAL_OFFSET_TABLE_, %eax
leal foo@GOTOFF(%eax), %eax <-- ???
ret
I don't know why this is as indirect as it is on x86-32, but this really is what gcc generates. The ??? is a GOT reference.
For example, imagine you have code like this:
If you build it as position-independent code, on x86-64, you get: Here, "foo" turns into the offset from the end of the LEA instruction to the variable foo. The x86 address calculation engine does the rest.On x86-32, there's no IP-relative addressing, so you can't use LEA like this, and you get a mess, which I've trimmed into readability:
I don't know why this is as indirect as it is on x86-32, but this really is what gcc generates. The ??? is a GOT reference.Edit: fixed formatting