Switching to Protected mode with gnu as

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
thebayliff
Posts: 4
Joined: Sat Nov 05, 2022 2:55 am

Switching to Protected mode with gnu as

Post by thebayliff »

I am forcing myself to try to use `ld` and `as` as built by binutils and not use nasm. This worked fine while in real mode, but now that I am trying to enter protected mode I am encountering issues. The main problem is I can't get `as` to compile the same way that nasm does.

The minimum example I could find is a nasm bootsector I copied from http://3zanders.co.uk/2017/10/13/writin ... /boot2.asm.

Code: Select all

bits 16
org 0x7c00

boot:
	mov ax, 0x2401
	int 0x15
	mov ax, 0x3
	int 0x10
	cli
	lgdt [gdt_pointer]
	mov eax, cr0
	or eax,0x1
	mov cr0, eax
	jmp CODE_SEG:boot2
    nop
gdt_start:
	dq 0x0
gdt_code:
	dw 0xFFFF
	dw 0x0
	db 0x0
	db 10011010b
	db 11001111b
	db 0x0
gdt_data:
	dw 0xFFFF
	dw 0x0
	db 0x0
	db 10010010b
	db 11001111b
	db 0x0
gdt_end:
gdt_pointer:
	dw gdt_end - gdt_start
	dd gdt_start

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

bits 32
boot2:
	mov ax, DATA_SEG
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	mov esi,hello
	mov ebx,0xb8000
.loop:
	lodsb
	or al,al
	jz halt
	or eax,0x0100
	mov word [ebx], ax
	add ebx,2
	jmp .loop
halt:
	cli
	hlt
hello: db "Hello world!",0

times 510 - ($-$$) db 0
dw 0xaa55
When I convert this to att syntax and gnu assembler directives (and using -Ttext 0x7c00 with my linker command to do the equivalent of [org])

Code: Select all

.code16
start:
jmp boot

gdt_start:
    .quad 0x0
gdt_code:
    .word 0xFFFF
    .word 0x00
    .byte 0x00
    .byte 0b10011010
    .byte 0b11001111
    .byte 0x00
gdt_data:
    .word 0xFFFF
    .word 0x00
    .byte 0x00
    .byte 0b10010010
    .byte 0b11001111
    .byte 0x00
gdt_end:
gdt_pointer:
	.word gdt_end - gdt_start
	.word 0x00
    .word gdt_start

CODE_SEG = gdt_code - gdt_start
DATA_SEG = gdt_data - gdt_start

boot:
    mov $0x2401, %ax
    int $0x15
    mov $0x3, %ax
    int $0x10
    cli
    lgdt gdt_pointer
    mov %cr0, %eax
    or $0x1, %eax
    mov %eax, %cr0

    jmp $CODE_SEG, $boot2

.code32
boot2:
    mov $DATA_SEG, %ax
    mov %ax, %ds
    mov %ax, %es
    mov %ax, %fs
    mov %ax, %gs
    mov %ax, %ss
    mov $hello, %esi
    mov $0xb8000, %ebx
.loop:
	lodsb
	or %al,%al
	jz halt
	or $0x0100, %eax
	movw %ax, (%ebx)
	add $2, %ebx
	jmp .loop
halt:
	cli
	hlt
hello: .string "Here we are!!"

    . = start + 510 # Fill the rest of the 510 bytes with '\0'.
    .byte 0x55
    .byte 0xAA 
However these produce very different assembly code:

Code: Select all

#nasm
0000000 01b8 cd24 b815 0003 10cd 0ffa 1601 7c38
0000010 200f 66c0 c883 0f01 c022 3eea 087c 9000
0000020 0000 0000 0000 0000 ffff 0000 9a00 00cf
0000030 ffff 0000 9200 00cf 0018 7c20 0000 b866
0000040 0010 d88e c08e e08e e88e d08e 6abe 007c
0000050 bb00 8000 000b 08ac 74c0 0d0d 0100 0000
0000060 8966 8303 02c3 eeeb f4fa 6548 6c6c 206f
0000070 6f77 6c72 2164 0000 0000 0000 0000 0000
0000080 0000 0000 0000 0000 0000 0000 0000 0000
*
00001f0 0000 0000 0000 0000 0000 0000 0000 aa55
0000200

#gnu as
0000000 1eeb 0000 0000 0000 0000 ffff 0000 9a00
0000010 00cf ffff 0000 9200 00cf 0018 0000 7c02
0000020 01b8 cd24 b815 0003 10cd 0ffa 1601 7c1a
0000030 200f 66c0 c883 0f01 c022 3fea 087c 6600
0000040 10b8 8e00 8ed8 8ec0 8ee0 8ee8 bed0 7c6b
0000050 0000 00bb 0b80 ac00 c008 0d74 000d 0001
0000060 6600 0389 c383 eb02 faee 48f4 7265 2065
0000070 6577 6120 6572 2121 0000 0000 0000 0000
0000080 0000 0000 0000 0000 0000 0000 0000 0000
*
00001f0 0000 0000 0000 0000 0000 0000 0000 aa55
0000200
The main difference between the two codes is that for gnu as, I had to relocate the gdt to be above the boot function which means I also had to add a jmp at the beginning of the code and add a label to ensure that at the end we are the right number of bytes.

If I try to put the gdt in the same spot as nasm, the long jump gives me an error: Error: can't handle non absolute segment in `jmp'.

I'm trying really hard (for no particular reason) to avoid using nasm and only use gnu gcc tools, but I'm getting stuck on switching to protected mode.

Any thoughts/suggestions?
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Switching to Protected mode with gnu as

Post by iansjack »

They’re obviously going to produce different code as you have rearranged the sections. But you don’t say if the code works and, if not, what happens.
Octocontrabass
Member
Member
Posts: 5562
Joined: Mon Mar 25, 2013 7:01 pm

Re: Switching to Protected mode with gnu as

Post by Octocontrabass »

thebayliff wrote:

Code: Select all

	.word gdt_end - gdt_start
	.word 0x00
    .word gdt_start
Why is ".word 0x00" in there?

Shouldn't that be ".long gdt_start" instead of ".word gdt_start"?
thebayliff
Posts: 4
Joined: Sat Nov 05, 2022 2:55 am

Re: Switching to Protected mode with gnu as

Post by thebayliff »

iansjack wrote:They’re obviously going to produce different code as you have rearranged the sections. But you don’t say if the code works and, if not, what happens.
Apologies, the code from nasm works when compiled and it outputs on qemu a screen with the correct text "Hello World!" in blue text. The gnu as code triple faults and causes qemu to reboot.
Octocontrabass wrote:
thebayliff wrote:

Code: Select all

	.word gdt_end - gdt_start
	.word 0x00
    .word gdt_start
Why is ".word 0x00" in there?

Shouldn't that be ".long gdt_start" instead of ".word gdt_start"?
This seems to be the problem. I knew it was supposed to be the nasm equivalent of dd, but I thought I was being smart by adding the high order byte being all 0s first (shouldn't try to outsmart anyone in assembly.)

I now have working protected mode execution using gnu as. Thanks all!
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Switching to Protected mode with gnu as

Post by iansjack »

Just as a matter of interest, you could keep the program order the same as the example you are copying by defining the _SEG values at the head of the listing. Although you calculate them (which is the problem - gnu as doesn’t allow you to use them before you calculate them) you know that they are 0x08 and 0x10.
Octocontrabass
Member
Member
Posts: 5562
Joined: Mon Mar 25, 2013 7:01 pm

Re: Switching to Protected mode with gnu as

Post by Octocontrabass »

thebayliff wrote:I thought I was being smart by adding the high order byte being all 0s first
It would have worked, but x86 is little-endian. You have to put the low-order word first.
Post Reply