2

The below code is an snippet from the 2nd stage loader program. It is running in Real Mode and gets called by the bootloader program. The problem is that GDB is showing wrong addresses to variables allocated in stack.

|------|------|------|
|Name  |GDB   |Actual|
|------|------|------|
|char a|0x7be7|0x7bfb|
|char b|0x7be6|0x7bfa|
|------|------|------|

Source:

__attribute__((noreturn)) 
void __main()
{
    char a = 'A';
    char b = 'B';
    while(1);
}

Disassembly:

00008000 <__main>:
    8000:   66 55                   push   ebp
    8002:   66 89 e5                mov    ebp,esp
    8005:   66 83 ec 10             sub    esp,0x10
    8009:   67 c6 45 ff 41          mov    BYTE PTR [ebp-0x1],0x41  <--- char a
    800e:   67 c6 45 fe 42          mov    BYTE PTR [ebp-0x2],0x42  <--- char b
    8013:   eb fe                   jmp    8013 <__main+0x13>

This file gets loaded at physical location 0x8000 by a custom bootloader.

QEMU and GDB

> qemu-system-i386 -fda build/boot.flp -s -S
> gdb loader.sym
  target remote localhost:1234
  set architecture i386
  b __main
  c
  s
  s
  p &a
    0x7be7 "" <-- Not EBP - 1
  p &b
    0x7be6 "" <-- Not EBP - 2

  info reg
  eax            0x0                 0
  ecx            0x0                 0
  edx            0x7de3              32227
  ebx            0x8000              32768
  esp            0x7bec              0x7bec
  ebp            0x7bfc              0x7bfc  <-- char a is at 0x7bfb and char b is at 0x7bfa
  esi            0x0                 0
  edi            0x0                 0
  eip            0x8013              0x8013 <__main+19>
  eflags         0x202               [ IF ]
  cs             0x0                 0
  ss             0x0                 0
  ds             0x0                 0
  es             0x0                 0
  fs             0x0                 0
  gs             0x0                 0

I do not really understand what is going on. Could it be the -m16 option?

Compilation

gcc -std=c99 \
    -nostartfiles \
    -c \
    -g \
    -ffreestanding \
    -fno-pie \
    -fno-stack-protector \
    -m16 \
    -march=i386 \
    -Wpedantic \
    -Wextra \
    -Wall \
    -O0  bootloader/x86/phase2/loader.c -o $TEMPDIR/loader.o || exit

ld -m elf_i386 --nmagic --script=build/loader.ld $TEMPDIR/loader.o -o $TEMPDIR/loader.lo || exit

objcopy --only-keep-debug $TEMPDIR/loader.lo $SYMDIR/loader.sym||exit
objcopy -O binary $TEMPDIR/loader.lo $OBJDIR/LOADER.flt||exit

Linker Script (build/loader.ld)

ENTRY (__main)
SECTIONS
{
    . = 0x8000;                 /* Loader is loaded at 0x0000:0x8000 */
    .text :AT(0x0)
    {
        *.o (.text);
    }
    .data :
    {
        *.o (.data);
        *.o (.bss);
        *.o (.rodata);
    }
    /DISCARD/ : 
    {
        *(.eh_frame)
    }
}

Things I tried:

  1. Using -m32 instead of -m16 and .code16gcc at the top of the C file.
  2. Verified that GCC, GDB works perfectly when compilling a native application.
  3. Used --oformat binary option in ld instead of objcopy

PS:

  • GCC Version: 8.3.0
  • GNU ld Version: 2.31.1
  • Linux Debian 10
Arjob Mukherjee
  • 387
  • 1
  • 10
  • after `mov ebp,esp` your ebp gets the value of esp. I am not sure if esp wasn't changed before calling your __main. Can you check it? – rhaport Sep 07 '20 at 09:58
  • @rhaport the `MOV` statement worked. The `EBP` before the __main call was `0x00` and 'ESP` before the call was `0x7B80` (Thars the Stack TOP) – Arjob Mukherjee Sep 07 '20 at 10:57
  • You removed the `.eh_frame` section in which the debugger is relying on to determine the position of stack data. Consider using the `-fno-asynchronous-unwind-tables` option instead of discarding `.eh_frame` in the linker script – Michael Petch Sep 07 '20 at 14:11
  • Don't use `-m32` as that would generate 32-bit code. `-m16` is correct if you want to generate 32-bit code that runs in 16-bit real mode. `.code16gcc` was what was used prior to the option `-m16` being added. If your GCC supports `-m16` use it instead of `.code16gcc`. GCC 4.9.x+ should support `-m16`. – Michael Petch Sep 07 '20 at 14:26
  • @Micheal Petch: Am I doing something outside GCC spec, writing real mode part of 2nd Stage Bootloader is C, instead of Assembly. – Arjob Mukherjee Sep 07 '20 at 14:37
  • Well the regular versions of GCC don't create code that will run on on 286 and earlier processors. GCC's generated code even with `-m16` will almost certainly need a 386+ processor. GCC wasn't really ever designed with 16-bit code in mind and `-m16` is a serious hack to shoehorn 32-bit like code to run in 16-bit real mode. GDB's debugger also has problems debugging 16-bit real mode code especially if you intend to debug assembly code. GDB doesn't support proper debugging when segments are non-zero. GCC will also generate addresses that may exceed the 16-bit segment limits. – Michael Petch Sep 07 '20 at 14:45
  • Basically GDB isn't designed to debug 16-bit real mode code, and GCC wasn't ever designed to generate true 16-bit code. There is an [experimental fork of GCC](https://github.com/tkchia/gcc-ia16) that is intended to generate code that can be run on processors like the 8086/80186/80286 but it has bugs that may make cause problems. – Michael Petch Sep 07 '20 at 14:49
  • @MichaelPetch using `dwarf-2` debug option, it worked for the example provided above. But part of the code is written in assembly and `NASM` do not generate `dwarf-2` output, so mixing C and assembly is still causing same problems. I am guessing, I will use Assembly language for the 16bit parts. – Arjob Mukherjee Sep 07 '20 at 19:26
  • I would recommend trying to use the other option I mentioned above If the NASM instruction or the actual assembly instructions Rather than change the DWARF debugging type use `-fno-asynchronous-unwind-tables` instead. Your problem with looking at NASM code (or assembly code for that matter) may be partially related to this: https://stackoverflow.com/a/62522147/3857942 . I mentioned above that GDB wasn't designed to debug code running in real mode. – Michael Petch Sep 07 '20 at 20:24
  • You may have to use `set architecture i8086` when running GDB and you may need to create an XML file like this answer: https://stackoverflow.com/a/62522147/3857942 . But unless I saw what you are observing or you asked another question I am just guessing. Your have GDB decoding as i386 but it really is i8086. The instructions appearing in the debugger aren't necessarily the ones being executed. – Michael Petch Sep 07 '20 at 20:25

0 Answers0