0

I'm writing an OS and I want to switch to long mode. The problem is that my cross compiler (x86_64-elf-gcc) is generating position independent code even if I pass the -fno-pic option.

boot.S

/* boot.S - bootstrap the kernel */
/* https://www.kernel.org/doc/Documentation/x86/boot.txt */
    

#include <boot/param.h>
#include <asm/control_registers.h>
    
    /* 32-bit startup code. */
    .code32
    .text
    .global _start
    .type _start, @function
_start:
    /* Clear DF flag */
    cld
    
    /* Setup stack */
    movl    $stack_top, %esp

    /* Check Multiboot2 */
    call    check_multiboot2
    testl   %eax, %eax
    jnz wrong_multiboot2

    /* Check CPUID */
    call    check_cpuid
    testl   %eax, %eax
    jnz no_cpuid

    /* Check if CPU can run in long mode */
    call    check_longmode
    testl   %eax, %eax
    jnz no_longmode
    
    /* TODO: Check the KEEP_SEGMENTS flag. */   

    /* Update the Global Descriptor Table with 64-bit segments */
    lgdt    gdt64

    /* Enable PAE */
    movl    %cr4, %eax
    orl $(1 << X86_CR4_PAE_BIT), %eax
    movl    %eax, %cr4

    /* TODO: Build the structure for paging. */
    
    /* TODO: Transition to long mode. */

    movl    $0x2f4b2f4f, 0xb8000
    
    cli
1:  hlt
    jmp 1b
wrong_multiboot2:
    movl    $'0, %eax
    jmp error
no_cpuid:
    movl    $'1, %eax
    jmp error
no_longmode:
    movl    $'2, %eax   
error:
    movl    $0x4f524f45, (0xb8000)
    movl    $0x4f3a4f52, (0xb8004)
    movl    $0x4f204f20, (0xb8008)
    movb    %al, (0xb800a)
1:  hlt
    jmp 1b

    
#include "check.S"

    
    .section .rodata
    /* GDT */
gdt64:
    .word   gdt_end - gdt - 1   /* limit */
    .quad   gdt         /* base */
gdt:
    .quad   0           /* Null Descriptor */
    .quad   0x00af9a000000ffff  /* Kernel Mode Code Segment */
    .quad   0x00cf92000000ffff  /* Kernel Mode Data Segment */
    .quad   0x00affa000000ffff  /* User Mode Code Segment */
    .quad   0x00cff2000000ffff  /* User Mode Data Segment */
                    /* TODO: Task State Segment */
gdt_end:    

    
    .bss
    /* Stack */
    .balign 4
stack_bottom:
    .fill   STACK_SIZE, 1, 0
stack_top:

check.S

/* check.S : check for multiboot2, cpuid, long mode */

#include <boot/multiboot2.h>
    
/* Check multiboot2 */
check_multiboot2:   
    cmpl    $MULTIBOOT2_BOOTLOADER_MAGIC, %eax
    jne .Lcheck_wrong_multiboot2
    xorl    %eax, %eax
    ret
.Lcheck_wrong_multiboot2:
    movl    $1, %eax
    ret

/* Check if CPUID is supported by attempting to flip the ID bit (bit 21) in
   the FLAGS register. If we can flip it, CPUID is available. */
check_cpuid:
    pushf
    push    $0
    popf
    
    /* Copy FLAGS in to EAX via stack */
    pushfl
    popl    %eax

    /* Copy to EBX as well for comparing later on */
    movl    %eax, %ebx

    /* Flip the ID bit */
    xorl    $(1 << 21), %eax

    /* Copy EAX to FLAGS via the stack */
    pushl   %eax
    popfl

    /* Copy FLAGS back to EAX (with the flipped bit if CPUID is supported) */
    pushfl
    popl    %eax

    /* Restore FLAGS from the old version stored in ECX (i.e. flipping the ID bit
       back if it was ever flipped). */
    /* push ecx */

    /* Compare EAX and EBX. If they are equal then that means the bit wasn't
    flipped, and CPUID isn't supported. */
    cmpl    %eax, %ebx
    jz  .Lcheck_no_cpuid
    popf
    xorl    %eax, %eax
    ret
.Lcheck_no_cpuid:
    popf
    movl    $1, %eax
    ret

/* Check if CPU can support long mode (64 bits) */
check_longmode:
    pushf
    push    $0
    popf
    
    /* test if extended processor info in available */
    /* implicit argument for cpuid */
    movl    $0x80000000, %eax
    /* get highest supported argument */
    cpuid
    /* it needs to be at least 0x80000001 */
    cmpl    $0x80000001, %eax     
    jb  .Lcheck_no_longmode

    /* use extended info to test if long mode is available */
    /* argument for extended processor info */
    movl    $0x80000001, %eax     
    cpuid
    /* test if the LM-bit is set in the D-register */
    testl $(1 << 29), %edx
    /* If it's not set, there is no long mode */
    jz  .Lcheck_no_longmode
    popf
    xorl    %eax, %eax  
    ret
.Lcheck_no_longmode:
    popf
    movl    $1, %eax
    ret

I compile with x86_64-elf-gcc -Wall -Wextra -nostdlib -lgcc -mno-red-zone -ffreestanding -std=gnu99 -fno-pic -c src/boot/boot.S -o build/boot.o -I include

Here is the objdump of boot.o

0000000000000000 <_start>:
   0:   fc                      cld    
   1:   bc 00 00 00 00          mov    $0x0,%esp
   6:   e8 6d 00 00 00          call   78 <check_multiboot2>
   b:   85 c0                   test   %eax,%eax
   d:   75 30                   jne    3f <wrong_multiboot2>
   f:   e8 74 00 00 00          call   88 <check_cpuid>
  14:   85 c0                   test   %eax,%eax
  16:   75 2e                   jne    46 <no_cpuid>
  18:   e8 8b 00 00 00          call   a8 <check_longmode>
  1d:   85 c0                   test   %eax,%eax
  1f:   75 2c                   jne    4d <no_longmode>
  21:   0f 01 15 00 00 00 00    lgdt   0x0(%rip)        # 28 <_start+0x28>
  28:   0f 20 e0                mov    %cr4,%rax
  2b:   83 c8 20                or     $0x20,%eax
  2e:   0f 22 e0                mov    %rax,%cr4
  31:   c7 05 00 80 0b 00 4f    movl   $0x2f4b2f4f,0xb8000(%rip)        # b803b <stack_top+0xb403b>
  38:   2f 4b 2f 
  3b:   fa                      cli    
  3c:   f4                      hlt    
  3d:   eb fd                   jmp    3c <_start+0x3c>

000000000000003f <wrong_multiboot2>:
  3f:   b8 30 00 00 00          mov    $0x30,%eax
  44:   eb 0c                   jmp    52 <error>

0000000000000046 <no_cpuid>:
  46:   b8 31 00 00 00          mov    $0x31,%eax
  4b:   eb 05                   jmp    52 <error>

000000000000004d <no_longmode>:
  4d:   b8 32 00 00 00          mov    $0x32,%eax

0000000000000052 <error>:
  52:   c7 05 00 80 0b 00 45    movl   $0x4f524f45,0xb8000(%rip)        # b805c <stack_top+0xb405c>
  59:   4f 52 4f 
  5c:   c7 05 04 80 0b 00 52    movl   $0x4f3a4f52,0xb8004(%rip)        # b806a <stack_top+0xb406a>
  63:   4f 3a 4f 
  66:   c7 05 08 80 0b 00 20    movl   $0x4f204f20,0xb8008(%rip)        # b8078 <stack_top+0xb4078>
  6d:   4f 20 4f 
  70:   a2 0a 80 0b 00 f4 eb    movabs %al,0x3dfdebf4000b800a
  77:   fd  

0000000000000078 <check_multiboot2>:
  78:   3d 89 62 d7 36          cmp    $0x36d76289,%eax
  7d:   75 03                   jne    82 <check_multiboot2+0xa>
  7f:   31 c0                   xor    %eax,%eax
  81:   c3                      ret    
  82:   b8 01 00 00 00          mov    $0x1,%eax
  87:   c3                      ret    

0000000000000088 <check_cpuid>:
  88:   9c                      pushf  
  89:   6a 00                   push   $0x0
  8b:   9d                      popf   
  8c:   9c                      pushf  
  8d:   58                      pop    %rax
  8e:   89 c3                   mov    %eax,%ebx
  90:   35 00 00 20 00          xor    $0x200000,%eax
  95:   50                      push   %rax
  96:   9d                      popf   
  97:   9c                      pushf  
  98:   58                      pop    %rax
  99:   39 c3                   cmp    %eax,%ebx
  9b:   74 04                   je     a1 <check_cpuid+0x19>
  9d:   9d                      popf   
  9e:   31 c0                   xor    %eax,%eax
  a0:   c3                      ret    
  a1:   9d                      popf   
  a2:   b8 01 00 00 00          mov    $0x1,%eax
  a7:   c3                      ret    

00000000000000a8 <check_longmode>:
  a8:   9c                      pushf  
  a9:   6a 00                   push   $0x0
  ab:   9d                      popf   
  ac:   b8 00 00 00 80          mov    $0x80000000,%eax
  b1:   0f a2                   cpuid  
  b3:   3d 01 00 00 80          cmp    $0x80000001,%eax
  b8:   72 13                   jb     cd <check_longmode+0x25>
  ba:   b8 01 00 00 80          mov    $0x80000001,%eax
  bf:   0f a2                   cpuid  
  c1:   f7 c2 00 00 00 20       test   $0x20000000,%edx
  c7:   74 04                   je     cd <check_longmode+0x25>
  c9:   9d                      popf   
  ca:   31 c0                   xor    %eax,%eax
  cc:   c3                      ret    
  cd:   9d                      popf   
  ce:   b8 01 00 00 00          mov    $0x1,%eax
  d3:   c3                      ret    

We can see that movl $0x2f4b2f4f, 0xb8000 is compiled into movl $0x2f4b2f4f,0xb8000(%rip) so I can't properly write into the VGA text buffer starting at 0xb8000.

I'm also taking any other advice.

R0M2
  • 1
  • 3
    `-fno-pic` has no effect on your assembly, it's for compiler generated code. gcc is just invoking the assembler for you, try 1) adding `-v` option to see what arguments it passes and/or 2) invoking `as` directly. On my native linux I do not get a rip-relative address. – Jester Mar 31 '22 at 21:34
  • 3
    Pass `-m32` to have gcc generate a 32 bit object file. You are disassembling 32 bit code as 64 bit. Rule of thumb: if you use `.code32` you better know exactly what you are doing. Usually this directive is an indication that you are doing something wrong. – fuz Mar 31 '22 at 21:56
  • @fuz Good catch. – Jester Mar 31 '22 at 22:19
  • 1
    In general, `-fPIC` / `-fno-pic` is a C->asm code-gen option, not an assembler or linker option. A common mistake is trying to link x86-64 asm that [uses 32-bit *absolute* addresses](https://stackoverflow.com/questions/43367427/32-bit-absolute-addresses-no-longer-allowed-in-x86-64-linux) into a 64-bit PIE (which GCC makes by default), and the error message tells you to recompile with `-fPIC`. Doing that of course doesn't help in the other direction either. – Peter Cordes Apr 01 '22 at 00:59
  • 2
    But what's happening here isn't GAS changing your asm into position-independent references to the same address (notice it's `0xb8000(%rip)` with a non-zero RIP), you're just disassembling in the wrong mode as Fuz said. There's no metadata to indicate which parts of your file used `.code32` and which `.code64`, so it's up to you to use disassembler options that match the part of the file you care about, like `objdump -M i386` or `objdump -M x86-64` – Peter Cordes Apr 01 '22 at 01:01
  • Thank you. I will now use the `-m32` option or `i686-elf-gcc` but I have another problem now : `src/boot/boot.S:81: Error: cannot represent relocation type BFD_RELOC_64`. This is this line `.quad gdt /* base */` – R0M2 Apr 01 '22 at 14:38

0 Answers0