1

I have the following assembly:

_start:    
    mov $strings,   %rbx
    mov $1,         %r12d
  print_loop:
    mov %rbx,       %rdi

It is simple enough, but here is what it shows in gdb for each of these three lines/instructions:

  1. mov $strings, %rbx

    0x00000000004000c4  ? mov    $0x6000ea,%rbx # memory address of 'strings'
    
    ─── Stack ──────────────────────────────────────────────────────────────────────────────────────────────────────
    [0] from 0x00000000004000c4 in _start
    
  2. mov $1, %r12d

    0x00000000004000cb  ? mov    $0x1,%r12d
    
    ─── Stack ───────────────────────────────────────────────────────────────────────────────────────────────────────
    [0] from 0x00000000004000cb in _start
    
  3. But then on the third instruction -- the first one after the label -- things look wonky:

    0x00000000004000d1  ? mov    %rbx,%rdi
    
    ─── Stack ───────────────────────────────────────────────────────────────────────────────────────────────────────
    [0] from 0x00000000004000d1 in print_loop
    [1] from 0x0000000000000001
    [2] from 0x00007fffffffe5aa
    [3] from 0x0000000000000000
    

What does that mean exactly, and why does it show things like that? It seems like the stack should still show one line:

[0] from 0x00000000004000d1 in print_loop
- or - 
[0] from 0x00000000004000d1 in _start
samuelbrody1249
  • 4,379
  • 1
  • 15
  • 58

1 Answers1

2

What does that mean exactly,

It means that GDB believes that you are executing inside print_loop function, and that it thinks this function was called from some code at address 0x1, which was called from address 0x7fffffffe5aa, etc.

and why does it show things like that?

It is confused.

It seems like the stack should still show one line:

Correct.


Now, your next question is probably "why does GDB get confused?".

The answer is somewhat complicated.

On platforms which do not use a dedicated frame pointer register, such as x86_64, there is no general way for GDB to unwind the stack, and it needs help from the compiler to do so. Compilers create "unwind tables", which GDB interprets to perform stack unwinding.

Since you are writing in assembly, and did not use .cfi_... directives, your code does not have any unwind descriptors.

In the absence of unwind descriptors, GDB can only guess, and here guesses incorrectly.

To fix this, you can provide unwind descriptors, something like this (untested):

_start:
    .cfi_startproc
    .cfi_undefined(rip)     # no unwinding past this function    
    mov $strings,   %rbx
    mov $1,         %r12d
  print_loop:
    mov %rbx,       %rdi
...
    .cfi_endproc

and that should unconfuse GDB. Documentation of various .cfi directives can be found here.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • So does GDB special-case `_start` to not try to unwind the stack while executing inside it? Because it knows that the stack contents will be `argc` and `argv[]`.... – Peter Cordes Sep 18 '20 at 16:59
  • Is `.cfi` short for "Call Frame" ?Or what are those directives generally related to? – samuelbrody1249 Sep 18 '20 at 18:50
  • 1
    @samuelbrody1249 I believe `CFI` stands for "call frame info". – Employed Russian Sep 18 '20 at 19:38
  • @samuelbrody1249: A quick google for `site:stackoverflow.com cfi directive` finds a bunch of stuff including [What are CFI directives in Gnu Assembler (GAS) used for?](https://stackoverflow.com/q/2529185) which answers that question. And see https://sourceware.org/binutils/docs/as/CFI-directives.html. In general, try googling your questions before asking other people to spend their time answering comments, especially broad questions. – Peter Cordes Sep 18 '20 at 23:27