-1

I've made my own kernel. It has 32-bit pm, 16-bit pm, and 16-bit real mode code. I use Qemu to emualate an i686. And when i debug the mode switch parts, the code runs and works. However, the disassembly from disassemble command, and from gdb-dashboard still assume 32bit default opcode sizes, and so it writes garbages.

I'm now forced to compare: qemu instructions logs the object dumped kernel binary and the assembly source code. For code execution

and gdb for machine states (register values, memory values...) This naturally slow down productivity. And being able to just use gdb to look at the disassembly would be nice.

set architecture i8086 doesn't work.

#!/bin/bash

set -eou pipefail
ARG1="${1:-}"

# Define directories
BUILD_DIR="build"
ISO_DIR="isodir"

CFLAGS=("-std=gnu23" "-ffreestanding" "-Wall" "-Wextra")
CFLAGS16=("-std=gnu99" "-ffreestanding" "-Wall" "-Wextra")
LDFLAGS=("-ffreestanding" "-nostdlib" "-lgcc")
NASM_FLAGS32=("-f" "elf32")
NASM_FLAGS16=("-f" "elf")

DEBUG_OPT_LVL="-O0"
RELEASE_OPT_LVL="-O0"
QEMU_DBG_FLAGS=()

if [[ "$ARG1" == "debug" ]]; then
    echo "Debug mode enabled"
    CFLAGS+=("$DEBUG_OPT_LVL" "-g" "-DDEBUG")
    CFLAGS16+=("$DEBUG_OPT_LVL" "-g" "-DDEBUG")
    LDFLAGS+=("-g")
    NASM_FLAGS32+=("-g" "-F" "dwarf")
    NASM_FLAGS16+=("-g" "-F" "dwarf")
    QEMU_DBG_FLAGS+=("-s" "-S")
else
    echo "In normal mode, $RELEASE_OPT_LVL optimisation"
    CFLAGS+=("$RELEASE_OPT_LVL")
    CFLAGS16+=("$RELEASE_OPT_LVL")
    LDFLAGS+=("$RELEASE_OPT_LVL")
fi

# Create necessary directories
mkdir -p "$BUILD_DIR" "$ISO_DIR/boot/grub"

# The variable for the dirs:
KERNEL="kernel"
REAL16_WRAPPERS="real16_wrappers"
REAL_FUNC="realmode_functions"
GDT="gdt"
IDT="idt"
STDIO="stdio"
STDLIB="stdlib"
OTHER="other"
CODE_ANALYSIS="runtime_code_analysis"

INCLUDE_DIRS=(
    "$KERNEL"
    "$REAL16_WRAPPERS"
    "$REAL_FUNC"
    "$GDT"
    "$IDT"
    "$STDIO"
    "$STDLIB"
    "$OTHER"
    "$CODE_ANALYSIS"
)

# Build SUPER_INCLUDE as an array of -I arguments
SUPER_INCLUDE=()
for dir in "${INCLUDE_DIRS[@]}"; do
    SUPER_INCLUDE+=("-I$dir")
done

# Assemble the bootloader assembly
nasm "${NASM_FLAGS32[@]}" "$KERNEL/boot_intel.asm" -o "$BUILD_DIR/boot.o"
i686-elf-gcc "${CFLAGS[@]}" "${SUPER_INCLUDE[@]}" -c "$KERNEL/kernel.c" -o "$BUILD_DIR/kernel.o"

# Compile the print functions.
i686-elf-gcc "${CFLAGS[@]}" -c "$STDIO/string_helper.c" -o "$BUILD_DIR/string_helper.o"
i686-elf-gcc "${CFLAGS[@]}" -c "$STDIO/bit_hex_string.c" -o "$BUILD_DIR/bit_hex_string.o" -std=gnu99 "-I$STDIO" "-I$STDLIB"
i686-elf-gcc "${CFLAGS[@]}" -c "$STDIO/vga_terminal.c" -o "$BUILD_DIR/vga_terminal.o" "-I$STDIO" "-I$OTHER"
i686-elf-gcc "${CFLAGS[@]}" -c "$STDIO/stdio.c" -o "$BUILD_DIR/stdio.o" "-I$STDIO" "-I$OTHER" "-I$STDLIB"

#compile misc helper functions
i686-elf-gcc "${CFLAGS[@]}" -c "$STDLIB/stdlib.c" -o "$BUILD_DIR/stdlib.o"

i686-elf-gcc "${CFLAGS[@]}" -c "$GDT/f1_binary_operation.c" -o "$BUILD_DIR/f1_binary_operation.o" "-I$STDIO" "-I$GDT"
i686-elf-gcc "${CFLAGS[@]}" -c "$GDT/f3_segment_descriptor_internals.c" -o "$BUILD_DIR/f3_segment_descriptor_internals.o" "-I$STDIO" "-I$GDT" "-I$STDLIB"
i686-elf-gcc "${CFLAGS[@]}" -c "$GDT/gdt.c" -o "$BUILD_DIR/gdt.o" "-I$STDIO" "-I$GDT" "-I$STDLIB"

# Compile the CPU functionality activation part
i686-elf-gcc "${CFLAGS[@]}" -c "$IDT/idt.c" -o "$BUILD_DIR/idt.o" "-I$IDT" "-I$GDT" "-I$STDLIB" "-I$STDIO"
nasm "${NASM_FLAGS32[@]}" "$IDT/exception_handler.asm" -o "$BUILD_DIR/exception_handler.o"

i686-elf-gcc "${CFLAGS[@]}" -c "$OTHER/virtual_memory.c" -o "$BUILD_DIR/virtual_memory.o"
i686-elf-gcc "${CFLAGS[@]}" -c "$OTHER/pit_timer.c" -o "$BUILD_DIR/pit_timer.o"

i686-elf-gcc "${CFLAGS[@]}" -c "$CODE_ANALYSIS/address_getter.c" -o "$BUILD_DIR/address_getter.o" "-I$REAL_FUNC" "-I$REAL16_WRAPPERS" "-I$GDT" "-I$STDLIB"

# Compile the real mode 16 code and it's wrappers
i686-elf-gcc "${CFLAGS[@]}" -c "$REAL16_WRAPPERS/call_real16_wrapper.c" -o "$BUILD_DIR/call_real16_wrapper.o" "-I$STDIO" "-I$STDLIB" "-I$GDT"
nasm "${NASM_FLAGS16[@]}" "$REAL16_WRAPPERS/call_realmode_function_wrapper16.asm" -o "$BUILD_DIR/call_realmode_function_wrapper16.o" "-I$REAL16_WRAPPERS"
nasm "${NASM_FLAGS32[@]}" "$REAL16_WRAPPERS/call_realmode_function_wrapper32.asm" -o "$BUILD_DIR/call_realmode_function_wrapper32.o" "-I$REAL16_WRAPPERS"
ia16-elf-gcc "${CFLAGS16[@]}" -c "$REAL_FUNC/realmode_functions.c" -o "$BUILD_DIR/realmode_functions.o"

# Link the kernel and generate the final binary
printf "\n\n====== Start of Linking =====\n\n"

BUILD_OBJECTS=(
    "$BUILD_DIR/boot.o"
    "$BUILD_DIR/kernel.o"

    "$BUILD_DIR/stdlib.o"

    "$BUILD_DIR/address_getter.o"

    "$BUILD_DIR/string_helper.o"
    "$BUILD_DIR/bit_hex_string.o"
    "$BUILD_DIR/vga_terminal.o"
    "$BUILD_DIR/stdio.o"

    "$BUILD_DIR/idt.o"
    "$BUILD_DIR/exception_handler.o"

    "$BUILD_DIR/virtual_memory.o"
    "$BUILD_DIR/pit_timer.o"

    "$BUILD_DIR/f1_binary_operation.o"
    "$BUILD_DIR/f3_segment_descriptor_internals.o"
    "$BUILD_DIR/gdt.o"

    "$BUILD_DIR/call_realmode_function_wrapper32.o"
    "$BUILD_DIR/call_realmode_function_wrapper16.o"
    "$BUILD_DIR/realmode_functions.o"
    "$BUILD_DIR/call_real16_wrapper.o"
)

i686-elf-gcc -T linker.ld -o "$BUILD_DIR/myos.bin" "${LDFLAGS[@]}" "${BUILD_OBJECTS[@]}"

printf "\n\n====== End of Linking =====\n\n"

objdump -D -h "$BUILD_DIR/myos.bin" >"$BUILD_DIR/myos.dump"

# Check if the kernel is multiboot-compliant
if grub-file --is-x86-multiboot "$BUILD_DIR/myos.bin"; then
    echo "Multiboot confirmed"
else
    echo "The file is not multiboot"
    exit 1
fi

# Copy the kernel binary and GRUB configuration to the ISO directory
cp "$BUILD_DIR/myos.bin" "$ISO_DIR/boot/myos.bin"
cp "$ISO_DIR/grub.cfg" "$ISO_DIR/boot/grub/grub.cfg"

# Create the ISO image
grub-mkrescue -o "$BUILD_DIR/myos.iso" "$ISO_DIR"

echo "ISO created successfully: $BUILD_DIR/myos.iso"

# start qemu using the binary, bypassing grub.
# -no-reboot: don't reset if the kernel crash
# -d in_asm, int, cpu_reset: Logs every instruction executed by cpu, every interupt and cpu reset events
# -D qemu_instr.log : The log file (redirect -d)
# -serial stdio: redirect COM1 serial port to your terminal

# ===== Pick one of those two
# -kernel "$BUILD_DIR/myos.bin" \
# -cdrom "$BUILD_DIR/myos.iso" \
qemu-system-i386 \
    -cdrom "$BUILD_DIR/myos.iso" \
    -no-reboot \
    "${QEMU_DBG_FLAGS[@]}" \
    -d in_asm,int,cpu_reset \
    \
    -serial stdio & # -D qemu_instr.log \

# qemu-system-i386 -kernel ./build/myos.bin & # or do this to use the binary directly # -cdrom "$BUILD_DIR/myos.iso" # -kernel "$BUILD_DIR/myos.bin" \

QEMU_PID=$!

# Give QEMU a second to start up
sleep 1

# Launch VNC viewer
vncviewer localhost:5900

# After you close the VNC viewer, kill QEMU
kill $QEMU_PID 2>/dev/null

The build script.

then I:

i868-elf-gdb build/myos.bin

(gdb) target remote :1234 and then go on to debug as usual

6
  • 4
    Isn't this "how to use modern gdb" question instead of retrocomputing? You are also using modern Qemu and writing new software. Commented Nov 15 at 22:55
  • 3
    This is something that gdb is just not very good at. Try bochs instead for this part. Its built-in debugger handles different CPU modes much more gracefully. Commented Nov 16 at 1:35
  • 1
    I usually don't bother with GDB’s disassembler and use QEMU’s – i.e. mon x/10i $pc Commented Nov 16 at 4:48
  • Sounds to me like a fine StackOverflow question. Commented Nov 16 at 11:59
  • My last post about Modern Qemu and Mode change was published on stack overflow. But moved to Retrocomputing by a mods. The people were nice and gave good answers. So I figured to post here again too. Really am being ping pong all over the place. Lol. Commented Nov 16 at 22:46

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.