NASM Forum > Using NASM

LEA or MOV

(1/3) > >>

munair:
I'm fairly new to NASM. I began using it more than a year ago for a 32bits compiler (sharpbasic.com).

Going over some 32 bits code, I found two ways to get the address of a string to be printed, i.e. both give the same output. What I used so far is:


--- Code: ---  ' address of string buffer
  emittl("mov     ebx, _sb_buf11")
  ' convert integer to string (expect address in ebx)
  emittl("call    _sb_intstr")
  ' print
  emittl("call    _sb_print")

--- End code ---

But to obtain the address of the string buffer LEA works as well:


--- Code: ---emittl("lea     ebx, dword [_sb_buf11]")

--- End code ---

Which is the better approach?

fredericopissarra:
It depends on the mode. In x86-64 mode LEA is smaller and with less colateral effects then MOV to obtain the address of an object in .data, .bss or .rodata sections because uses RIP relative addresses. This:

--- Code: ---  mov rax,x   ; puts the address of x in RAX
  lea  rax,[x]  ; same thing...
--- End code ---
Both instructions have REX prefix because of their target (RAX), but the linear address of 'x' must be known in runtime and, since it is a 64 bits value, `mov rax,x` is encoded as a 10 bytes instruction (48 B8 for `mov eax`, plus 8 bytes for the address). 'LEA' is different here... '[ x ]' is usually encoded as a RIP relative address, so only 32 bits of the offset is encoded in the instruction and, since the address is relative to the address of the next instruction, no relocation is needed (the 'x' in MOV is encoded as a constant that must be added to the program base address, which is known only after the image is loaded... consider ASLR, for example!). 'LEA', here, is encoded only in 7 bytes, putting less pressure on L1I cache and internal reordering buffer of the processor (and prefetch queue).

So... LEA is preferable in x86-64 mode to get the address of static objects. In i386 mode (32 bits) it makes no difference.

munair:
Thanks a lot for your explanation fredericopissarra. It is very helpful.

BTW, I realized I started this topic in the wrong place. Any moderator is welcome to put it in the right place. ;)

fredericopissarra:
Another tip: In i386 mode avoid using EBP as stack "base" pointer. This practice is common in real mode (16 bits) because only BP or BX can be used as base pointers in an effective address calculation. In 386 protected mode any register (but not EBP ou EFLAGS) can be used, so to access objects on the stack you can use ESP directly.

In i386 mode EAX, EBX, ECX, EDX, ESI, EDI and EBP can be used (depending on the calling convention). If you use EBP as a substitute to ESP than you ended with only 6 GPRs available for your routines, instead of 7.

So, the prologue:

--- Code: ---  push ebp
  mov ebp,esp
--- End code ---
and the epilogue:

--- Code: ---  pop ebp
  ret
--- End code ---
Can be (and should be) avoided.

Let's say you build a function taking two integers. Like this, in C:

--- Code: ---int f(int a, int b) { return a + b; }
--- End code ---
Instead of doing:

--- Code: ---f:
  push ebp
  mov  ebp,esp
  mov  eax,[ebp+8]
  add  eax,[ebp+12]
  pop  ebp
  ret
--- End code ---
You can write:

--- Code: ---f:
  mov eax,[esp+4]
  add eax,[esp+8]
  ret
--- End code ---

munair:
That is a really great tip! And I should take that to the 64 bits version later on as well, I guess (I'm certainly not there yet).

Navigation

[0] Message Index

[#] Next page

Go to full version