GDB, показывающий неправильный адрес для локальных переменных

#c #gcc #linker #gdb #osdev

#c #gcc #компоновщик #gdb #экранное меню

Вопрос:

Приведенный ниже код представляет собой фрагмент из программы загрузки 2-го этапа. Он выполняется в Real Mode и вызывается программой загрузчика. Проблема в том, что GDB показывает неправильные адреса для переменных, выделенных в стеке.

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

Источник:

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

Разборка:

 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>
  

Этот файл загружается в физическом расположении 0x8000 пользовательским загрузчиком.

QEMU и 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 amp;a
    0x7be7 "" <-- Not EBP - 1
  p amp;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
  

Я действительно не понимаю, что происходит. Может ли это быть -m16 вариантом?

Сборник

 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
  

Скрипт компоновщика (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)
    }
}
  

Что я пробовал:

  1. Используя -m32 вместо -m16 и .code16gcc в верхней части файла C.
  2. Проверено, что GCC, GDB отлично работает при компиляции собственного приложения.
  3. Используется --oformat binary опция в ld вместо objcopy

PS:

  • Версия GCC: 8.3.0
  • Версия GNU ld: 2.31.1
  • Linux Debian 10

Комментарии:

1. после mov ebp,esp вашего ebp получает значение esp. Я не уверен, что esp не был изменен перед вызовом вашего __main. Вы можете это проверить?

2. @rh port MOV инструкция сработала. EBP Перед вызовом __main был 0x00 и ‘ESP` перед вызовом был 0x7B80 (на ВЕРШИНЕ стека)

3. Вы удалили .eh_frame раздел, в котором отладчик полагается на определение положения данных стека. Рассмотрите возможность использования -fno-asynchronous-unwind-tables опции вместо отбрасывания .eh_frame в скрипте компоновщика

4. Не используйте -m32 , так как это приведет к генерации 32-битного кода. -m16 правильно, если вы хотите сгенерировать 32-разрядный код, который выполняется в 16-разрядном реальном режиме. .code16gcc это то, что использовалось до добавления опции -m16 . Если ваш GCC поддерживает, -m16 используйте его вместо .code16gcc . GCC 4.9.x должен поддерживать -m16 .

5. @Micheal Petch: Я делаю что-то вне спецификации GCC, записывая часть реального режима загрузчика 2-го этапа на C, а не на Assembly.