3

I'm aware what the callq instruction does; it is described in this answer. The instruction should have one parameter, which should be the address of the function called.

However, when disassembling an object file I see the following line showing a callq instruction with two values instead of one parameter:

  1e:   e8 00 00 00 00          callq  23 <main+0x19>

I assume that 23 is the return address and <main+0x19> is the address of the function to be called. However, the function which is called is not located at address main+0x19.

What is the meaning of the two values 23 and main+0x19 shown in the disassembly of the callq instruction?


Original C code:

#include <stdio.h>

void fun(int a)
{
}

int main()
{
    int a = 1234;
    fun(a);
}

objdump -D main.o main.asm Disassambly code:

0000000000000000 <fun>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
   7:   90                      nop
   8:   5d                      pop    %rbp
   9:   c3                      retq   

000000000000000a <main>:
   a:   55                      push   %rbp
   b:   48 89 e5                mov    %rsp,%rbp
   e:   48 83 ec 10             sub    $0x10,%rsp
  12:   c7 45 fc 03 00 00 00    movl   $0x3,-0x4(%rbp)
  19:   8b 45 fc                mov    -0x4(%rbp),%eax
  1c:   89 c7                   mov    %eax,%edi
  1e:   e8 00 00 00 00          callq  23 <main+0x19>
  23:   b8 00 00 00 00          mov    $0x0,%eax
  28:   c9                      leaveq 
  29:   c3                      retq   

Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
Edward
  • 333
  • 2
  • 12
  • 2
    I don't think that is a duplicate, because I think the misunderstandings in the question are not addressed in those answers. – prl May 14 '19 at 03:16
  • I voted for re-open because neither the allegedly "duplicate" question asks about the parameter(s) of the instruction, nor one of the answers there answers it. – Martin Rosenau May 14 '19 at 05:02
  • I also modified the question in a way how -**I**- understood it. @Edward: I hope I understood your problem correctly and the question is still asking what you wanted to ask. – Martin Rosenau May 14 '19 at 05:19
  • 1
    @MartinRosenau: agreed. This isn't about `callq` vs. `call`, it's about understanding how x86 `call rel32` works in the first place and how disassemblers helpfully calculate absolute targets from the relative offset. And how unlinked object files work. – Peter Cordes May 14 '19 at 05:35
  • @PeterCordes I think the user has problems understanding the two values `23` and `main+0x19`. So I modified the question accordingly. The user has problems understanding that there is some relocation at address 0x1F. ... and a problem understanding that `objdump -D` does not show relocations. – Martin Rosenau May 14 '19 at 05:38
  • @MartinRosenau: yes, exactly. That's why I upvoted prl's answer. But good point about showing relocations: I use `objdump -drwC -Mintel`. – Peter Cordes May 14 '19 at 06:00
  • I'm sorry, I use stackoverlflow fist time, so I might have clicked on the "duplicate" link by mistake, although i didn't want to do that. – Edward May 14 '19 at 06:35

1 Answers1

7

The callq instruction has only one operand, not two as implied in the question. The disassembler displays it in two forms, as an address and as a symbol + offset.

You are looking at disassembly of an unlinked object file. Since the file being disassembled isn't linked, the destination address shown isn't the address of fun. The assembler puts 0 in the operand field of the instruction and creates a relocation record for the linker to fill in the offset to the final address of the destination.

The operand of the call instruction is an offset, relative to the address of the next instruction after the call. So a value of 0 in the operand field causes the disassembler to display the address of the next instruction as the destination of the call. In the disassembly shown, that is address 23.

If you make fun a static function, the assembler may fill in the true offset of the function, since it won't require relocation, and you will see it in the disassembly. (This may depend on the exact tools and options you are using.)

If you disassemble a linked executable, the disassembler will show the true destination address of the call.

You can also use objdump -drwC (optionally with -Mintel) to show relocations as comments on instructions when disassembling a file like a .o that has some placeholders with relocations.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
prl
  • 11,716
  • 2
  • 13
  • 31
  • disassembling a linked PIE executable, the default on newer Ubuntu but not the OP's 16.04, will still assume a base address of `0` for the whole executable. But yes, relative offsets will be filled in correctly. When you actually run it, the base address for the whole thing will change, but not the relative offsets or the `symbol+x` way of showing the jump targets. – Peter Cordes May 14 '19 at 03:42
  • I use objdump -DrwC -Mintel main.o the asm code becomed that: 1e: e8 00 00 00 00 call 23
    1f: R_X86_64_PC32 fun-0x4 23: b8 00 00 00 00 mov eax,0x0 why is relocate addr "fun-0x4" instead of "fun"
    – Edward May 14 '19 at 07:51
  • @Edward That object file has not yet been relocated. As you see, all the operand bytes to `call` are `00 00 00 00`, i.e. their values have not yet been filled in. Observe a linked binary to see correct values. – fuz May 14 '19 at 09:39
  • The linker fills in the relative offset of the relocation symbol relative to the first byte of the field being filled in, but the instruction needs the offset to the destination relative the next instruction, which is a difference of 4 bytes. – prl May 14 '19 at 09:52
  • @prl Thanks, I found answer for all my questions in 《Computer Systems:A Programmer's Perspective》 – Edward May 15 '19 at 01:56