1 year ago

#361200

test-img

R0M2

Why "GCC" ignores -fno-pic for assembly code

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.

assembly

gcc

cross-compiling

boot

osdev

0 Answers

Your Answer

Accepted video resources