Gdb "ignores" breakpoints when linking text to 0xC0000000

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
hrniels
Member
Member
Posts: 53
Joined: Wed Nov 05, 2008 5:18 am
Location: Marburg, Germany

Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by hrniels »

Hi everybody!

A few weeks ago I started to build a small OS with C for x86. Some basic stuff (gdt set up, paging, interrupts, printing something to screen,...) seems to work fine now, but since a few days I have a problem with debugging my kernel. It's a little bit strange that it worked before, but however, perhaps I'm missing something :)

Now to my problem:
It seems that as soon as I link the text of the kernel above 0xC0000000 gdb does ignore the breakpoints I set. That means I can set the breakpoint, gdb stores it with the correct address, but it does not break.

My linker script is:

Code: Select all

OUTPUT_FORMAT("elf32-i386")
ENTRY(loader)

SECTIONS
{
	. = 0x100000;

	.setup :
	{
		*(.setup)
	}

	. += 0xC0000000;

	.text : AT(ADDR(.text) - 0xC0000000)
	{
		*(.text)
	}

	.data ALIGN (4096) : AT(ADDR(.data) - 0xC0000000)
	{
		*(.data)
		*(.rodata*)
	}

	.bss ALIGN (4096) : AT(ADDR(.bss) - 0xC0000000)
	{
		*(COMMON*)
		*(.bss*)
	}
}
And my kernel (just the important parts of the asm-file):

Code: Select all

[BITS 32]      												; 32 bit code

[extern main]

; Multiboot constants
MULTIBOOT_PAGE_ALIGN	equ 1<<0
MULTIBOOT_MEMORY_INFO	equ 1<<1
MULTIBOOT_HEADER_MAGIC	equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS	equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
MULTIBOOT_CHECKSUM	equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)

; Multiboot header (needed to boot from GRUB)
ALIGN 4
KernelStart:
multiboot_header:
	dd		MULTIBOOT_HEADER_MAGIC
	dd		MULTIBOOT_HEADER_FLAGS
	dd		MULTIBOOT_CHECKSUM

; the kernel entry point
loader:
	; here is the trick: we load a GDT with a base address
	; of 0x40000000 for the code (0x08) and data (0x10) segments
	lgdt	[setupGDT]
	mov		ax,0x10
	mov		ds,ax
	mov		es,ax
	mov		fs,ax
	mov		gs,ax
	mov		ss,ax

	; jump to the higher half kernel
	jmp		0x08:higherhalf

higherhalf:
	; from now the CPU will translate automatically every address
	; by adding the base 0x40000000

	mov		esp, sys_stack								; set up a new stack for our kernel

	push	eax														; push Multiboot Magicnumber onto the stack
	push	ebx														; push address of Multiboot-Structure
	call	main													; jump to our C kernel ;)

	; just a simple protection...
	jmp		$

[section .setup]

; our GDT for the setup-process
setupGDT:
	; GDT size
	dw		setupGDTEntries - setupGDTEntriesEnd - 1
	; Pointer to entries
	dd		setupGDTEntries

; the GDT-entries
setupGDTEntries:
	; null gate
	dd 0,0

	; code selector 0x08: base 0x40000000, limit 0xFFFFFFFF, type 0x9A, granularity 0xCF
	db 0xFF, 0xFF, 0, 0, 0, 10011010b, 11001111b, 0x40

	; data selector 0x10: base 0x40000000, limit 0xFFFFFFFF, type 0x92, granularity 0xCF
	db 0xFF, 0xFF, 0, 0, 0, 10010010b, 11001111b, 0x40
setupGDTEntriesEnd:

[section .bss]

resb 0x1000
sys_stack:
	; our kernel stack
The main-function (c-code) doesn't do anything usefull, so I don't post it here.

I compile and link the kernel with:

Code: Select all

nasm -g -f elf -o ../build/kernel_asm.o src/kernel.asm
gcc -g -O2 -fno-inline -ffreestanding -Wall -ansi -nostdlib -nostartfiles -nodefaultlibs -o ../build/kernel.o -c src/kernel.c
ld -T src/ld.conf -o ../build/kernel.bin ../build/kernel_asm.o ../build/kernel.o
And I debug the kernel with (build/disk.img contains the kernel.bin at "/"):

Code: Select all

qemu -serial stdio -s -S -no-kqemu -fda build/disk.img > log.txt 2>&1 &
sleep 0.5
gdb --symbols build/kernel.bin
And in gdb:

Code: Select all

(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000fff0 in ?? ()
(gdb) b main
Breakpoint 1 at 0xc0100060: file src/kernel.c, line 10.
(gdb) c
Continuing.
It doesn't matter if I try to break at "loader" or "main", gdb does not break. But as soon as I link the text to 0x100000 (and adjust the kernel so that it works) everything is fine.

I've searched quite a long time to find a solution and tried many things but without success :(
Does anybody know what I'm doing wrong or what I can do to solve the problem?
Thanks in advance!

hrniels
hrniels
Member
Member
Posts: 53
Joined: Wed Nov 05, 2008 5:18 am
Location: Marburg, Germany

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by hrniels »

Oh, I just figured out that it does work in other "call-hierarchies" (if I've interpreted it correctly). That means breakpoints work for example in any routine that is called within the interrupt-handling.
But I haven't found a way to get it working within the loader- and main-function :(
Has anybody an idea?

hrniels
User avatar
Walling
Member
Member
Posts: 158
Joined: Mon Dec 04, 2006 6:06 am
Location: Berlin, Germany

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by Walling »

Have you tried disassembling the memory at location 0xC0100060 to see that your code is actually loaded? Maybe the QEMU debugging stub only reports physical addresses and doesn't support virtual memory/segmentation.
hrniels
Member
Member
Posts: 53
Joined: Wed Nov 05, 2008 5:18 am
Location: Marburg, Germany

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by hrniels »

Yes, if I do for example a "disassemble main" in gdb I get the code of the main-function at the correct address.

hrniels
User avatar
Walling
Member
Member
Posts: 158
Joined: Mon Dec 04, 2006 6:06 am
Location: Berlin, Germany

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by Walling »

And I suppose you get the right address, when you "print &main" ? Have you tried single-stepping (or single-instruction) through the setup code and "print $eip" / "info r" and see what happens?
hrniels
Member
Member
Posts: 53
Joined: Wed Nov 05, 2008 5:18 am
Location: Marburg, Germany

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by hrniels »

Walling wrote:And I suppose you get the right address, when you "print &main" ?
Yep
Walling wrote:Have you tried single-stepping (or single-instruction) through the setup code and "print $eip" / "info r" and see what happens?
hm...I don't know how to get there. Grub does quite a lot at the beginning so I think it makes no sense to step through unless I know the address to break at (the code directly before my kernel starts). But I don't know how to disassemble the grub stage1/stage2 files because objdump complains with "File format not recognized". Is there another way?

hrniels
User avatar
Velko
Member
Member
Posts: 153
Joined: Fri Oct 03, 2008 4:13 am
Location: Ogre, Latvia, EU

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by Velko »

Had the same problem with similar setup (QEMU + GDB). Looks like GDB (at least breakpoints) is not working right if GDT base is not zero.

Try to set breakpoint at physical address: 0x100060 instead of 0xc0100060. It worked for me.
If something looks overcomplicated, most likely it is.
hrniels
Member
Member
Posts: 53
Joined: Wed Nov 05, 2008 5:18 am
Location: Marburg, Germany

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by hrniels »

hm..no, that doesn't work for me :/

hrniels
User avatar
Walling
Member
Member
Posts: 158
Joined: Mon Dec 04, 2006 6:06 am
Location: Berlin, Germany

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by Walling »

I tried figure out the problem with the code you posted while cooking.

Well... I had to add a "[global loader]" to kernel.asm in order to export the loader symbol to the linker, but I guess that is not the problem. I used yasm instead of nasm, because it support dwarf2 debugging format. I haven't got nasm's stabs format to work with GDB. Maybe I just use an old nasm that doesn't support dwarf2. Next I saw that both loader and main gave some address at 0xC00000xx. Why is loader not included in the .setup section? Actually setting a breakpoint "b *0x10000C" worked and stopped at what I guess is loader. If I move the .setup section in kernel.asm I can set a breakpoint at loader (because the address is 0x10000C), but after that the virtual machine crashes. So my guess is you mixed up something with yours sections and symbols, or else I mixed up something, because I was cooking. 8)
hrniels
Member
Member
Posts: 53
Joined: Wed Nov 05, 2008 5:18 am
Location: Marburg, Germany

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by hrniels »

Thanks for your effort!

You are right. I can put loader into .setup instead of .text. So if I do it like the following:

Code: Select all

[section .setup]

; Multiboot header (needed to boot from GRUB)
ALIGN 4
KernelStart:
multiboot_header:
	dd		MULTIBOOT_HEADER_MAGIC
	dd		MULTIBOOT_HEADER_FLAGS
	dd		MULTIBOOT_CHECKSUM

; the kernel entry point
loader:
	; here is the trick: we load a GDT with a base address
	; of 0x40000000 for the code (0x08) and data (0x10) segments
	lgdt	[setupGDT]
	mov		ax,0x10
	mov		ds,ax
	mov		es,ax
	mov		fs,ax
	mov		gs,ax
	mov		ss,ax

	; jump to the higher half kernel
	jmp		0x08:higherhalf

[section .text]

higherhalf:
...
A breakpoint for "loader" works. Unfortunatly not for "main" :/

Additionally I've just tried to use bochs for that. The good news is that breakpoints seem to work everywhere. The bad news is that the code seems to be at the wrong place (while debugging, the kernel works like it should). That means if I disassemble any location where I should find some code it contains just 0xFFFFFFFF.
In the bochs-config-file I have:

Code: Select all

gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
Unfortunatly I haven't found any documentation for the *_base-options. What exactly is done with them or to what values should I set them? I have tried a few that came to my mind (0, c0100000, c0000000, 100000) but without success.
Or is there anything else I missed?

hrniels
User avatar
Walling
Member
Member
Posts: 158
Joined: Mon Dec 04, 2006 6:06 am
Location: Berlin, Germany

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by Walling »

Erhm.. where did you get the idea that setting the GDT base to 0x40000000 would put the kernel at 0xC0000000? AFAIK the GDT base is added to get the physical address. When you jump to higherhalf, you try to execute something at 0x40100028, which doesn't exists. QEMU crashes with this error (log.txt):'

Code: Select all

qemu: fatal: Trying to execute code outside RAM or ROM at 0x40100028

EAX=2bad0010 EBX=0002cd80 ECX=00000001 EDX=00000000
ESI=0002cef0 EDI=0002cef1 EBP=00067ee0 ESP=00067ed0
EIP=00100028 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 40000000 ffffffff 40cf9300
CS =0008 40000000 ffffffff 40cf9a00
SS =0010 40000000 ffffffff 40cf9300
DS =0010 40000000 ffffffff 40cf9300
FS =0010 40000000 ffffffff 40cf9300
GS =0010 40000000 ffffffff 40cf9300
LDT=0000 00000000 0000ffff 00008000
TR =0000 00000000 0000ffff 00008000
GDT=     0010003c 0000ffe7
IDT=     00000000 000003ff
CR0=60000011 CR2=00000000 CR3=00000000 CR4=00000000
CCS=00000045 CCD=00000000 CCO=LOGICL
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
Segmentation is not paging.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by Combuster »

Looks like the Higher_Half_With_GDT approach. Segmentation may not be the same, but it can be used to create an impression.

The problem is, you should use that only with higherhalf addresses, so use either HH:0xC0xxxxxx or linear:0x001xxxxx, but not HH:0x001xxxxx because you'd go off into nothingness that way.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
hrniels
Member
Member
Posts: 53
Joined: Wed Nov 05, 2008 5:18 am
Location: Marburg, Germany

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by hrniels »

As far as I have understood it:
The kernel is not "put" at 0xC0000000 but at the physical address 0x00100000. At the beginning paging is disabled, so there is just segmentation. GRUB configures the GDT as a flat address-space. The linker-script causes the linker to reference the main stuff of the kernel at 0xC0100000. The problem is that at the beginning I can't access anything at 0xC0100000 because it lies physically at 0x00100000 and this address passes segmentation without change. Therefore I introduce a setup-segment which is referenced at 0x00100000 and all stuff that is needed at the beginning to "correct" the GDT (at least temporary) will go there. So I setup the GDT with a base-address of 0x40000000 (0xC0100000 + 0x40000000 = 0x00100000).
Just for your information: As soon as paging is set up the GDT is reset to a flat address space.

Have I explained it correctly? :)
At least it works fine if I just run the kernel and don't try to debug...
Combuster wrote:The problem is, you should use that only with higherhalf addresses, so use either HH:0xC0xxxxxx or linear:0x001xxxxx, but not HH:0x001xxxxx because you'd go off into nothingness that way.
hm...sorry, I don't understand that completely. Where do I use "HH:0x001xxxxx"?

hrniels
User avatar
Walling
Member
Member
Posts: 158
Joined: Mon Dec 04, 2006 6:06 am
Location: Berlin, Germany

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by Walling »

Okay. The problem is you can put loader and GDT in .setup, but not higherhalf. When I put higherhalf in .text (so its address is 0xC0..something) it works. However as you said in the first post breakpoints doesn't works. If I put a breakpoint on loader, higherhalf and main, then it will break at loader and I can singlestep through the program. It will recognize the breakpoints at higherhalf and main when I singlestep. It doesn't work to "continue". I guess the reason is GDB assumes a base address 0.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Gdb "ignores" breakpoints when linking text to 0xC0000000

Post by Combuster »

hrniels wrote:Have I explained it correctly? :)
That'd be about right
Combuster wrote:The problem is, you should use that only with higherhalf addresses, so use either HH:0xC0xxxxxx or linear:0x001xxxxx, but not HH:0x001xxxxx because you'd go off into nothingness that way.
hm...sorry, I don't understand that completely. Where do I use "HH:0x001xxxxx"?

hrniels
HH means the higherhalf selector i.e. the one where base=0x40000000. What I get from Walling's dump is that the segments are set to a nonzero base, but the instruction pointer wasn't adjusted accordingly. i.e. you're accessing 0x001xxxxx with base 1G adding up to physical=0x401xxxxx, where there is obviously no memory installed.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Post Reply