c++assemblyx86-64ltrace

Different instruction address shown in ltrace and objdump


I use ltrace and objdump to analyse the simple code below. But I find there is a difference on instruction address shown between ltrace and objdump.

#include <iostream>
int main() {
    std::cout << "Hello";
    return 0;
}

As the following info, you can see that the address of [call std::basic_ostream] is [0x400789] in ltrace. (0x400789 is the address of the instruction "call", not std::basic_ostream)

binary@binary-VirtualBox:~/code/chapter5/test$ ltrace -i -C ./a.out
[0x4006a9] __libc_start_main(0x400776, 1, 0x7fff06c6ad28, 0x4007f0 <unfinished ...>
[0x4007b7] std::ios_base::Init::Init()(0x601171, 0xffff, 0x7fff06c6ad38, 160)             = 0
[0x4007cb] __cxa_atexit(0x400650, 0x601171, 0x601048, 0x7fff06c6ab00)                     = 0
[0x400789] std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)(0x601060, 0x400874, 0x7fff06c6ad38, 192) = 0x601060
[0x7f220180aff8] std::ios_base::Init::~Init()(0x601171, 0, 0x400650, 0x7f2201b96d10Hello)      = 0x7f2201f19880
[0xffffffffffffffff] +++ exited (status 0) +++

However, the address of [call std::basic_ostream] shown in objdump is [0x400784] and another instruction [mov eax,0x0] is on [0x400789]. The same is true for other "call" instructions.

0000000000400776 <main>:
  400776:       55                      push   rbp
  400777:       48 89 e5                mov    rbp,rsp
  40077a:       be 74 08 40 00          mov    esi,0x400874
  40077f:       bf 60 10 60 00          mov    edi,0x601060
  400784:       e8 d7 fe ff ff          call   400660 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
  400789:       b8 00 00 00 00          mov    eax,0x0
  40078e:       5d                      pop    rbp
  40078f:       c3                      ret    

I really want to know what causes the gap. Thank you a lot.


Solution

  • Those are return addresses (instruction after the call in the parent, the address which call pushes on the stack).

    ltrace can't know how long the instruction was that called a function, e.g. call reg with a function pointer is only 2 bytes vs. 5 for call rel32 vs. 6 for call [RIP + rel32] (memory-indirect call which GCC will use if you compile with -fno-plt.)

    Or if it was tail-called, execution would have reached it from a jmp or something, so even on an ISA with fixed-length instructions like MIPS or AArch64, ltrace still couldn't reliably print where it was called from. Best to not try to make too much stuff up and keep it simple, printing the address it can actually see on the callstack.