Debugging Assembler ?

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.
Post Reply
mbluett
Member
Member
Posts: 27
Joined: Fri Nov 21, 2008 5:45 pm

Debugging Assembler ?

Post by mbluett »

I am trying to develop a understanding of operating system basics. In the process, I have been fighting with several issues.

In order to see what is happening with various implementations, I have been trying to use GDB with QEMU (running on Windows XP).

I am using a basic bootloader (from the Reactos code) called "isoboot.asm". This routine loads a file called "setupldr.sys" to address 0x8000. This works fine.

I have created a "setupldr.sys" file based on code bits from other experimental operating systems. The objective is the following:

1. Enable A20
2. Load Flat-model GDT
2. Switch to PMode
3. Setup registers
4. Load a Kernel from the ISO file (where my bootloader and setupldr.sys came from)

My test "setupldr.sys" code is as follows:

Code: Select all

[BITS 16]

start:

; enable A20
    cli
    mov 	al, 0d1h            	; AT
    out 	64h, al
    mov 	al, 3
    out 	60h, al
    mov 	al, 2               	; PS/2
    out 	92h, al

; switch to protected mode
    lgdt 	[cs:GDTvalue]    	; load protected mode GDT
    mov 	eax, cr0			; read current cr0 settings
    or 	al, 1                	        ; set protected mode flag
    mov 	cr0, eax			; store modified cr0 settings
    jmp 	os_code:SetRegs       ; jump to 32 bit code


GDTvalue:         dw  GDT_l - GDT - 1    ; size of GDT - 1
			dd  GDT                      ; base of GDT
	
GDT: 			dd 0				
				dd 0				
os_code_l:		        dw 0ffffh, 0
                                db 0, 10011011b, 11001111b, 0
os_data_l:                dw 0ffffh, 0
                                db 0, 10010011b, 11001111b, 0
GDT_l:				

os_code        equ  os_code_l - GDT	; Pointer to 2nd 8-byte selector in the GDT

[BITS 32]                	 		         ; 32 bit protected mode code

; Setup the registers to make use of the newly created GDT
SetRegs:
    xor	ebx, ebx
    mov 	ds, ebx
    mov 	es, ebx
    mov 	fs, ebx
    mov 	gs, ebx
    mov 	ss, ebx

; Print Kernel Loading message
	mov     si, msgLoading
        call      Print 
	hlt
	
;************************************************;
;	Prints a string
;	DS=>SI: 0 terminated string
;************************************************;
Print:
			lodsb				; load next byte from string from SI to AL
			or	al, al			; Does AL=0?
			jz	PrintDone		; Yep, null terminator found-bail out
			mov	ah, 0eh		; Nope-Print the character
			int	10h
			jmp	Print			; Repeat until null terminator found
	PrintDone:
			ret				; we are done, so return 	
			
msgLoading  db 0x0D, 0x0A, "Loading Kernel... ", 0x0D, 0x0A, 0x00

If I run this routine it crashes. So, I have been trying to troubleshoot it with GDB. From my uses of GDB and from searching on the Net, I have seen that GDB does not work very well when debugging Assembler; however, through various tricks some things can be done (like only using "si" to step through code).

GDB is all I have, as the builtin debugging capabilities of Qemu are inadequate (or at least as far as I can tell by perusing the Qemu docs). For example, there appears to be no way to step through code.

GDB appears to show me what I would expect until I reach the instruction "jmp os_code:SetRegs".

In this case, GDB shows me it's disassembly of the instruction as follows:

jmp 0x17:0x8003e

First off I would have thought it should have shown 0x8 (instead of 0x17) as my understanding is that 8 references the 2nd 8-byte section of the GDT which happens to be my Code Segment selector. My code should produce a 0x8. Even if I replace the "os_code" with 8, it still shows up as 0x17.

0x17 happens to be the first word of the GDTR, but it makes no sense that the "jmp" instruction should be using that value. Why doesn't it use the value I have told it to use (0x8)?

Secondly, the 0x8003e makes no sense because I know the code is at 0x803e.

Given these "jmp" values it is no wonder it crashes. The problem is I don't understand how it is arriving at such odd values. So, is GDB showing me false information? Or, is there something else that I need to be doing in my code?

If GDB is the problem, what do others use as an alternative?

I would rather be using Olly Debugger as it is a MUCH better debugger, but I don't know how to make it connect to QEMU as does GDB. I suppose I could attach directly to the QEMU process, but then finding the address where my code starts becomes a significant issue.
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: Debugging Assembler ?

Post by neon »

Your print routine needs to be in 16bit code, not 32 bit code. Also, you are using your segment registers in real mode without ever setting them to anything. Either init them to 0 and use an ORG directive to create the correct offsets, or init them to the correct segments from the start. If your code is located at physical address 0x8000, then they should be set to 0x800.

Also, you cannot use your print function in protected mode as it uses BIOS interrupts. (And insure hardware interrupts are disabled [cli instruction] and keep in disabled at the beginning stages of protected mode).

Ill add more if I see any more problems...
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Re: Debugging Assembler ?

Post by bewing »

And Bochs + a nice GUI is a *much* better debugging environment than QEMU + GDB on a Windows system. I assume you don't have cygwin loaded, so if you PM me an email address that can accept an 800K file, I'll precompile a copy of bochs for you. You can singlestep right up to the point where it crashes, and it'll be obvious why it happened.
mbluett
Member
Member
Posts: 27
Joined: Fri Nov 21, 2008 5:45 pm

Re: Debugging Assembler ?

Post by mbluett »

I took your suggestion of setting the registers to 0x800. But, it still crashes and the "jmp" still shows "jmp 0x17:0x8004e".

0x17 is wrong and so is 0x8004e: It would expect it to be 0x8:0x804e.

(gdb) x /i $eip
0x802b: jmp 0x17:0x8004e
(gdb) x /i 0x8004e
0x8004e: add BYTE PTR [eax], al (This instruction does not appear in my code anywhere)
(gdb) x /i 0x804e
0x804e: xor ebx, ebx (This one does)


In other words, it makes no difference whether you set the registers or not. Lots of other code does it this way. I assume it is because when you are in 16-bit mode you have access to the entire area of memory from 0x0 to 0xfffff simply by referencing the correct address within that chunk of memory. Basically there are no segments not like there are in 32-bit mode.

However, I would agree with you regarding the Print routine. I wasn't really paying that close attention to that part of the code and forgot about interrupt routines not being available. But then I haven't really got that far as my code bombs before that and due to the incorrect addressing associated with the "jmp". The question is why is the addressing incorrect?

Here is the code as it stands now:

Code: Select all

[BITS 16]

start:
    xor 	bx, bx
    mov	bx, 800h
    mov 	ds, bx
    mov 	es, bx
    mov 	fs, bx
    mov 	gs, bx
    mov 	ss, bx	

; enable A20
    cli
    mov 	al, 0d1h            	; AT
    out 	64h, al
    mov 	al, 3
    out 	60h, al
    mov 	al, 2               	; PS/2
    out 	92h, al

; switch to protected mode
    lgdt 	[cs:GDTvalue]    	; load protected mode GDT
    mov 	eax, cr0			; read current cr0 settings
    or 		al, 1                	; set protected mode flag
    mov 	cr0, eax			; store modified cr0 settings
    sti						; enable interrupts
    jmp 	os_code:SetRegs         ; jump to 32 bit code


GDTvalue:       dw  GDT_l - GDT - 1        ; size of GDT - 1
				dd  GDT                ; base of GDT
	
GDT: 			dd 0				
				dd 0				
os_code_l:		        dw 0ffffh, 0
                               db 0, 10011011b, 11001111b, 0
os_data_l:                dw 0ffffh, 0
                               db 0, 10010011b, 11001111b, 0
GDT_l:				

os_code        equ  os_code_l - GDT	; Pointer to 2nd 8-byte selector in the GDT

[BITS 32]                	 		; 32 bit protected mode code

; Setup the registers to make use of the newly created GDT
SetRegs:
    xor	ebx, ebx
    mov 	ds, ebx
    mov 	es, ebx
    mov 	fs, ebx
    mov 	gs, ebx
    mov 	ss, ebx

mbluett
Member
Member
Posts: 27
Joined: Fri Nov 21, 2008 5:45 pm

Re: Debugging Assembler ?

Post by mbluett »

Disregard the stack not being correctly setup in the previous code listing. I realize it needs to be setup, but later for that.
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: Debugging Assembler ?

Post by neon »

Code: Select all

lgdt    [cs:GDTvalue]       ; load protected mode GDT
Why are you using cs here? Use either ds or just lgdt [GDTvalue] as ds will still be used.

Also, out of interest, does your jump work when you use jmp 0x8:SetRegs?

Finally, take out your final STI instruction and keep interrupts disabled. Not doing so will immediately triple fault after you enter protected mode do to the PIT firing an interrupt.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
mbluett
Member
Member
Posts: 27
Joined: Fri Nov 21, 2008 5:45 pm

Re: Debugging Assembler ?

Post by mbluett »

bewing wrote:And Bochs + a nice GUI is a *much* better debugging environment than QEMU + GDB on a Windows system. I assume you don't have cygwin loaded, so if you PM me an email address that can accept an 800K file, I'll precompile a copy of bochs for you. You can singlestep right up to the point where it crashes, and it'll be obvious why it happened.
I have already attempted to use Bochs. The problem is that it refuses to boot from the same ISO image that QEMU does boot from. I don't want to boot from a floppy image as I do not want to be restricted to 512 bytes.

In the Bochs config I have the following params in my bochsrc.bxrc file:

ata0: enabled=0, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=cdrom, path=MyOS.iso, status=inserted
boot: cdrom

I have left out other params that are present in the config file, but I don't think any of them would prevent it from working.

By the way, I do have CYGWIN setup.

When you say you would compile a Bochs setup for me, what is different in yours that is not available in the standard 2.3.7 release?

Comments?
Last edited by mbluett on Fri Nov 21, 2008 11:19 pm, edited 1 time in total.
mbluett
Member
Member
Posts: 27
Joined: Fri Nov 21, 2008 5:45 pm

Re: Debugging Assembler ?

Post by mbluett »

neon wrote:

Code: Select all

lgdt    [cs:GDTvalue]       ; load protected mode GDT
Why are you using cs here? Use either ds or just lgdt [GDTvalue] as ds will still be used.

Also, out of interest, does your jump work when you use jmp 0x8:SetRegs?

Finally, take out your final STI instruction and keep interrupts disabled. Not doing so will immediately triple fault after you enter protected mode do to the PIT firing an interrupt.
I did exactly as you suggested (I have done all of this before) and still no go.

No, the jump does not work when using 8:SetRegs (as mentioned in my original post). 0x8 and 8 amount to the same thing.

I used "cs" some time back in an attempt to try and resolve the incorrect addressing on the "jmp" that I have been seeing. It did not correct the problem either; however, I did notice a significant change when using it so I left it in. I can't remember exactly what the change was now.
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: Debugging Assembler ?

Post by neon »

Hm... Dont quite know what else it can be at this time. It may be helpful if you post your current code and provide a form of a log file (if possible) for us to track the problem down. If you can get it working in Bochs, that would be great :)
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Re: Debugging Assembler ?

Post by bewing »

mbluett wrote: ata0: enabled=0, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=cdrom, path=MyOS.iso, status=inserted
boot: cdrom
For ata0, enabled must be 1, not 0. 0 disables that device. If MyOS.iso is in the bochs main directory, it looks like it should work, otherwise.
When you say you would compile a Bochs setup for me, what is different in yours that is not available in the standard 2.3.7 release?
The GUI. Also, many people have trouble compiling in the bochs internal debugger. Especially if they are on Windows, and don't have cygwin. :wink:
mbluett
Member
Member
Posts: 27
Joined: Fri Nov 21, 2008 5:45 pm

Re: Debugging Assembler ?

Post by mbluett »

For ata0, enabled must be 1, not 0. 0 disables that device. If MyOS.iso is in the bochs main directory, it looks like it should work, otherwise.
I can't believe I missed that. Oh, well. Thanks very much for pointing it out.
The GUI. Also, many people have trouble compiling in the bochs internal debugger. Especially if they are on Windows, and don't have cygwin. :wink:
The 2.3.7 release comes with a file called "bochsdbg.exe" which is the version that has the debugger in it. No need to compile anything. Or am I missing something?

Thanks very much I have it working. Now to see if Bochs Debugger reports things differently and so far it seems that it does for example GDB always reported the "lgdt [GDTvalue]" as "lgdt [esi]"; whereas, Bochs reports it as "lgdt ds:0x2e". Ah that is better, Bochs is reporting the "jmp 8:SetRegs" as "jmp 08:004c". MUCH better.

Now I can get somewhere.

Thank you very much for getting me back on track.

GDB is definitely not useful unless you are debugging code where you have symbols available.
User avatar
Love4Boobies
Member
Member
Posts: 2111
Joined: Fri Mar 07, 2008 5:36 pm
Location: Bucharest, Romania

Re: Debugging Assembler ?

Post by Love4Boobies »

mbluett wrote:The 2.3.7 release comes with a file called "bochsdbg.exe" which is the version that has the debugger in it. No need to compile anything. Or am I missing something?
Yep :p You're missing the GUI front-end for the debugger that WindowsNT came up with and bewing heavily modified.
"Computers in the future may weigh no more than 1.5 tons.", Popular Mechanics (1949)
[ Project UDI ]
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Re: Debugging Assembler ?

Post by bewing »

The 2.3.7 release comes with a file called "bochsdbg.exe" which is the version that has the debugger in it. No need to compile anything. Or am I missing something?
Bochs has many options that can be built in at compile time. So a precompiled version will probably have limitations -- for example, your current exe has a debugger that is text-only with no GUI, as Love4Boobies says. Eventually you will probably find that some features you want to test are not supported in your exe. Also, it depends on where you find your "release". Clearly, you got yours from sourceforge, which is correct. Many other people find their copies in other places -- and they may be old versions without debugger support.
Post Reply