#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)
}
}
Что я пробовал:
- Используя
-m32
вместо-m16
и.code16gcc
в верхней части файла C. - Проверено, что GCC, GDB отлично работает при компиляции собственного приложения.
- Используется
--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.