Page 1 of 1

gdb code display not same as source code

Posted: Sun Aug 23, 2020 4:00 pm
by programmern00b
Hello, I am new to OS development and have been following the book recommended on the wiki called "Operating Systems: From 0 to 1", I have been following along and also implementing the code they use in the book on my computer. Everything was doing well with some slight changes needed from the books code, then section 8.5.3 I stumbled into a problem. The gdb debugger was not displaying the correct assembly code. I followed the books code precisely and do not understand why gdb is displaying the wrong assembly. I know now that the opcodes gdb displays at main are the magic numbers of the main program elf header, but why is this happening and how do I get it to display properly? If not possible is there an alternative solution?

bootloader.asm

Code: Select all

;*************************************************
; bootloader.asm 
; A Simple Bootloader
;*************************************************
bits 16
start: jmp  boot

;; constants and variable definitions
msg db "Welcome to My Operating System!", 0ah, 0dh, 0h

boot:

    cli ; no interrupts 
    cld ; all that we need to init
    
    mov ax, 0x0000

    ;; set buffer
    mov es, ax  
    mov bx, 0x0600

    mov al, 1   ; read one sector
    mov ch, 0   ; track 0
    mov cl, 2       ; sector to read
    mov dh, 0   ; head number
    mov dl, 0   ; drive number
        
    mov ah, 0x02    ; read sectors from disk    
    int 0x13      ; call the BIOS routine
    jmp 0x0000:0x0600   ; jump and execute the sector!
    
    hlt ; halt the system 

; We have to be 512 bytes. Clear the rest of the bytes with 0

times 510 - ($-$$) db 0
dw 0xAA55 ; Boot Signature
readelf -h main

Code: Select all

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x600
  Start of program headers:          52 (bytes into file)
  Start of section headers:          12888 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         3
  Size of section headers:           40 (bytes)
  Number of section headers:         12
  Section header string table index: 11
readelf -l main

Code: Select all

Elf file type is EXEC (Executable file)
Entry point 0x600
There are 3 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000000 0x00000000 0x00000000 0x00094 0x00094 R   0x4
  LOAD           0x000000 0x00000000 0x00000000 0x00094 0x00094 R   0x4
  LOAD           0x000100 0x00000600 0x00000600 0x00006 0x00006 R E 0x100

 Section to Segment mapping:
  Segment Sections...
   00     
   01     
   02     .text 
main.c

Code: Select all

void main(){}
objdump -z -M intel -S -D build/os/main

Code: Select all

Disassembly of section .text:

00000600 <main>:
void main(){}
 600:   55                      push   ebp
 601:   89 e5                   mov    ebp,esp
 603:   90                      nop
 604:   5d                      pop    ebp
 605:   c3                      ret    
when jumping to 0x0000:0x0600 in bootloader to main
this is what gdb displays;

Code: Select all

0x600 <main>    jg     0x647                                               │
│   0x602 <main+2>  dec    esp                                                 │
│   0x603 <main+3>  inc    esi                                                 │
│   0x604 <main+4>  add    DWORD PTR [ecx],eax                                 │
also here is how I compiled everything and started up qemu

Code: Select all

nasm -f elf bootloader.asm -F dwarf -g -o ../build/bootloader/bootloader.o
ld -m elf_i386 -T bootloader.lds ../build/bootloader/bootloader.o -o ../build/bootloader/bootloader.o.elf
objcopy -O binary ../build/bootloader/bootloader.o.elf ../build/bootloader/bootloader.o
gcc -ffreestanding -nostdlib -fno-pic -gdwarf-4 -m16 -ggdb3 -c main.c -o ../build/os/main.o
ld -m elf_i386 -nmagic -T os.lds  ../build/os/main.o -o ../build/os/main
dd if=/dev/zero of=disk.img bs=512 count=2880
2880+0 records in
2880+0 records out
1474560 bytes (1.5 MB, 1.4 MiB) copied, 0.0150958 s, 97.7 MB/s
dd conv=notrunc if=build/bootloader/bootloader.o of=disk.img bs=512 count=1 seek=0
1+0 records in
1+0 records out
512 bytes copied, 0.000127745 s, 4.0 MB/s
dd conv=notrunc if=build/os/main.o of=disk.img bs=512 count=$((8504/512))
seek=1
16+0 records in
16+0 records out
8192 bytes (8.2 kB, 8.0 KiB) copied, 0.000184251 s, 44.5 MB/s
qemu-system-i386 -machine q35 -fda disk.img -gdb tcp::26000 -S
here is the code I used for gdb

Code: Select all

set architecture i8086
target remote localhost:26000
b *0x7c00
set disassembly-flavor intel
layout asm
layout reg
symbol-file build/os/main
b main

Re: gdb code display not same as source code

Posted: Sun Aug 23, 2020 8:55 pm
by foliagecanine
The main problem is that your "main" file is in the ELF format. Computers only read binary format.

Here's the way I got your code working:
Since you didn't provide os.lds or bootloader.lds, I did it a different way.

Code: Select all

nasm -fbin bootloader.asm -o bootloader.o
gcc -ffreestanding -nostdlib -nostartfiles -fno-pic -fcf-protection=none -gdwar>
ld --oformat binary -T os.lds main.o -o os.bin
ld -T os.lds main.o -o os.sym
dd if=/dev/zero of=disk.img bs=512 count=2880
dd conv=notrunc if=bootloader.o of=disk.img bs=512 count=1 seek=0
dd conv=notrunc if=os.bin of=disk.img bs=512 seek=1
qemu-system-i386 -machine q35 -fda disk.img -gdb tcp::26000 -S
using the following linker file (os.lds):

Code: Select all

OUTPUT_ARCH(i8086)
ENTRY (main)

SECTIONS
{
	. = 0x600;
	.text : { *(.text) }
	.data : { *(.data) }
	.rodata : { *(.rodata) }
}
This compiles a bootable floppy. There is os.bin, which is the binary output that the computer should read and os.sym, which is the ELF output that your debugger should read.

However, the code you provided (bootloader.asm) does NOT work for two reasons:
- You haven't set up a stack of any sort, so right now it is undefined
- You used jmp instead of call, so when the main function tries to return, it will look in the undefined stack and find a random number (with QEMU it jumped to 0xf000d002, but it can vary by platform). I believe your intention was for the main function to return and execute the hlt instruction.

However, if you are worried whether gdb disassembles it,

Code: Select all

set architecture i8086
target remote localhost:26000
b *0x7C00
set disassembly-flavor intel
layout asm
layout reg
symbol-file os.sym
b main

--------

B+>0x600 <main>    push   %bp                                                 │
│   0x602 <main+2>  mov    %sp,%bp                                             │
│   0x605 <main+5>  nop                                                        │
│   0x606 <main+6>  pop    %bp                                                 │
│   0x608 <main+8>  retw                                                       │
│   0x60a               add    %al,(%eax)
I don't know how you want your build mechanism to go, but that is up to you.
Good luck with your OS!

Re: gdb code display not same as source code

Posted: Sun Aug 23, 2020 11:31 pm
by Octocontrabass
I think you should read section 8.5.1 again.

Re: gdb code display not same as source code

Posted: Mon Aug 24, 2020 9:18 am
by foliagecanine
Oops. I didn't notice that you were following a book #-o .

Octocontrabass is right though; on section 8.5 it tells you how to fix the linker script to load the "text" (code) section at 0x600.

However, do note that (as far as I can tell) that book is far from finished and won't get you too far.
There's also the two bugs I've mentioned in the previous post (no stack set up and jmp instead of call).

Good luck!

edit: spelling

Re: gdb code display not same as source code

Posted: Mon Aug 24, 2020 8:48 pm
by programmern00b
Sorry for the late reply,
fpr this step; gcc -ffreestanding -nostdlib -nostartfiles -fno-pic -fcf-protection=none -gdwar>
did you mean this; gcc -ffreestanding -nostdlib -nostartfiles -fno-pic -fcf-protection=none -gdwarf -c main.c, or was it supposed to be like that?
also sorry for leaving out the linker scripts I used;

os.lds;

Code: Select all

ENTRY(main);

PHDRS
{
  headers PT_PHDR FILEHDR PHDRS;
  headers2 PT_LOAD FILEHDR PHDRS;
  code PT_LOAD;
}

SECTIONS
{
  .text 0x600: ALIGN(0x100)  { *(.text)  } :code
  .data :  { *(.data)  }
  .bss :  { *(.bss) }
  /DISCARD/ : { *(.eh_frame) }
}
bootloader.lds

Code: Select all

OUTPUT(bootloader);

PHDRS
{
  headers PT_NULL;
  text PT_LOAD FILEHDR PHDRS ;
  data PT_LOAD ;
}


SECTIONS
{
  . = SIZEOF_HEADERS;
  .text 0x7c00:  {  *(.text)  } :text
  .data :  {  *(.data)  } :data
}
Thanks for the help so far.

Re: gdb code display not same as source code

Posted: Mon Aug 24, 2020 9:36 pm
by foliagecanine
programmern00b wrote:fpr this step; gcc -ffreestanding -nostdlib -nostartfiles -fno-pic -fcf-protection=none -gdwar>
did you mean this; gcc -ffreestanding -nostdlib -nostartfiles -fno-pic -fcf-protection=none -gdwarf -c main.c, or was it supposed to be like that?
Sorry, nano cut out that last bit

Code: Select all

gcc -ffreestanding -nostdlib -nostartfiles -fno-pic -fcf-protection=none -gdwarf-4 -m16 -ggdb3 -c main.c -o main.o
Since you're using a platform specific compiler, this disables GCC start files and disables fcf protections (they're meant for programs running on an OS, otherwise you'd have endbr32's everywhere)

----------------------------------------------------


The way the book says to do it is

os.lds:

Code: Select all

ENTRY(main);
PHDRS
{
    headers PT_PHDR FILEHDR PHDRS;
    code PT_LOAD;
}

SECTIONS
{
    .text 0x600: ALIGN(0x100) { *(.text) } :code
    .data : { *(.data) }
    .bss : { *(.bss) }
    /DISCARD/ : { *(.eh_frame) }
}
and in your build process add -nmagic:

Code: Select all

ld -m elf_i386 -nmagic -T os.lds main.o -o main
The way I did it (is probably not the best, but) literally strips out all of the ELF headers and data.
It produces two files, one binary and one with the symbols; the PC (or emulator) uses the binary file while gdb uses the symbol file.

----------------------------------------------------

Just a few tips:
- If you're running a Linux or BSD distro (in a virtual machine, or on real hardware) with a graphical envorionment, there's a software called DDD (Data Display Debugger). It has a horrible UI, but it's one of the easiest ways (for me at least) to see everything I'm debugging. It uses gdb for everything, but makes it much faster and easier to see and debug things.
- If you run QEMU with

Code: Select all

qemu-system-i386 <flags here> -s
it will be the same as

Code: Select all

qemu-system-i386 <flags here> -gdb tcp::1234
- The book you're using ends very soon. At this point, you'll either want to continue to create a Real Mode OS/bootloader or create a Protected Mode (or even Long Mode) OS. If you choose protected mode (like I did, although I did make a little tiny bootloader once), I'd recommend looking at the Bare Bones page.
- You could have probably put this thread in OS Development rather than General Programming, but I don't think it matters too much

----------------------------------------------------

(as a bonus, here's how I'd make bootloader.asm)

Code: Select all

;*************************************************
; bootloader.asm
; A Simple Bootloader
;*************************************************
bits 16
start: jmp  boot

;; constants and variable definitions
msg db "Welcome to My Operating System!", 0ah, 0dh, 0h

boot:

    cli ; no interrupts
    cld ; all that we need to init
   
    xor ax,ax ; This is the same thing as doing "mov ax,0"
    mov ss,ax ; Set stack segment to 0
    mov sp,0xFFFF ; Set stack to start at 0xFFFF and grow down

    ;; set buffer
    mov es, ax 
    mov bx, 0x0600

    mov al, 1   ; read one sector
    mov ch, 0   ; track 0
    mov cl, 2       ; sector to read
    mov dh, 0   ; head number
    mov dl, 0   ; drive number
       
    mov ah, 0x02    ; read sectors from disk   
    int 0x13      ; call the BIOS routine
    call 0x0000:0x0600   ; execute the sector, but provide a way for main to return
   
    hlt ; halt the system

; We have to be 512 bytes. Clear the rest of the bytes with 0

times 510 - ($-$$) db 0
dw 0xAA55 ; Boot Signature

Re: gdb code display not same as source code

Posted: Mon Aug 24, 2020 9:41 pm
by programmern00b
Okay GDB displays the correct code now, all that needed to change was I needed a binary file and symbol file for the kernel code if i'm not mistaken?
Thanks for all the help again!

Re: gdb code display not same as source code

Posted: Mon Aug 24, 2020 10:02 pm
by programmern00b
If you're running a Linux or BSD distro (in a virtual machine, or on real hardware) with a graphical envorionment, there's a software called DDD (Data Display Debugger).
Yes, I think I have stumbled upon this before to debug assembly code, I'll look more into it.

Also thanks for the tips on implementing a stack in my OS, remember to implement one in my future projects.

And thanks for giving me some directions on how to move forward, OS development seems like a big topic with lots of paths so thanks for the advice!