3

Having this simple assembly:

.text
    .globl main
str:
    .asciz "%i\n"
add:
    push %rbp
    mov %rsp, %rbp
    movl %esi, %eax
    addl %edi, %eax
    pop %rbp
    ret

main:
    mov $1, %esi
    mov $2, %edi
    lea add(%rip), %rax
    call %rax #what is wrong here? the rax should contain first instruction of add
    mov %eax, %esi
    xor %eax, %eax
    lea str(%rip), %rdi
    call printf
    xor %eax, %eax
    ret

I am getting error:

foo.s:17: Warning: indirect call without `*'

Why? The %rax should contain the address of function (as denoted in comment), and this is not c, where there are pointer with *, but register that contains an address. So what is wrong here?

Waqar
  • 8,558
  • 4
  • 35
  • 43
milanHrabos
  • 2,010
  • 3
  • 11
  • 45
  • 4
    AT&T syntax contains a lot of absurd rules, and one of them is requiring the `*` here. Just add the `*`, or switch to Intel syntax which is sane. – interjay Jul 09 '20 at 11:47
  • according to intel doc, the `call` instruction accepts an address. Which I am providing in the `rax` no matter if there is asterisk or not – milanHrabos Jul 09 '20 at 11:48
  • 4
    The Intel doc doesn't describe AT&T syntax. – interjay Jul 09 '20 at 11:49
  • @interjay how would it look in nasm? – milanHrabos Jul 09 '20 at 11:49
  • Simply `call rax`. – interjay Jul 09 '20 at 11:50
  • Some architectures and assemblers consider branching to data as dereferencing, and make you express that. This assembler is saying we can't branch to a register itself, but we can branch to the address in a register and then execute the instruction at that address (which is the dereference). – Erik Eidt Jul 09 '20 at 15:17

2 Answers2

6

Change

call %rax

to

call *%rax

This is not the same as call *(%rax). From @interjay's comment below:

call *%rax calls the address stored in rax (which is what OP wants to do), while call *(%rax) calls the address stored in memory pointed to by rax

The asterisk indicates that the call is an indirect call. It means call function stored inside %rax

Waqar
  • 8,558
  • 4
  • 35
  • 43
  • is it the same with a memory address? In case a function first instruction is not in `rax` but lets say in `0xdeadbeef`? Can I do `call *$deadbeef`? – milanHrabos Jul 09 '20 at 12:07
  • yes, see: https://stackoverflow.com/questions/9223756/what-does-an-asterisk-before-an-address-mean-in-x86-64-att-assembly and https://stackoverflow.com/questions/56979796/what-does-callq-rax-mean – Waqar Jul 09 '20 at 12:11
  • 4
    This is incorrect. `call *%rax` calls the address stored in `rax` (which is what OP wants to do), while `call *(%rax)` calls the address stored in memory pointed to by `rax`. – interjay Jul 09 '20 at 12:42
  • I actually tried myself with *%rax, but then added the parenthesis for style. I didn't know this made a difference. Thanks! I will fix the answer – Waqar Jul 09 '20 at 12:44
  • This answer is likely wrong. _*_ on x86-64 has nothing to do with direct/indirect. It used to be the 32-bit x86, but it is **not** true for 64-bit. – Michael O Apr 20 '21 at 01:25
  • Hmm, Maybe you are right. Can you give a reference where it says that is not true? – Waqar Apr 21 '21 at 20:23
5

AT&T syntax always requires a * on indirect calls, like call *%rax. This sets RIP = RAX.
call *(%rax, %rdi) would load from memory like RIP = mem_load(RDI+RAX).
call *foo(%rip) would load a new RIP from memory at the address foo, using a RIP-relative addressing mode.

When it's missing a * but still unambiguously an indirect call (because of a register operand or a (%reg) addressing mode), the assembler will infer that and warn you about it.


That AT&T syntax design avoids ambiguity between call foo (direct) and call *foo (memory indirect with an absolute-direct addressing mode).

Normally in 64-bit mode you'd use call *foo(%rip), but the ambiguity would still exist if call foo could mean memory indirect. And of course the syntax was designed before AMD64 existed.

In Intel syntax, memory-indirect call that loads a pointer into EIP/RIP from memory at the address foo with absolute addressing would be:

  • GAS Intel syntax: call [foo], call qword ptr foo, or call ds:foo

  • MASM: call [foo] might ignore the brackets so only qword ptr or ds: work.

  • NASM: call [foo] or call qword [foo]

So as you can see, MASM-style Intel-syntax (as used by GAS .intel_syntax noprefix) uses ds: or qword ptr to indicate that something is a memory operand, allowing call foo to be a normal E8 rel32 call direct.

Of course, as I said, in 64-bit mode you'd normally want to use call [RIP + foo] in GAS or call [rel foo] in NASM. (In actual MASM, I think RIP-relative is on by default.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847