Link error: relocation truncated to fit: R_386_16 against

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
OniDevilkin
Posts: 4
Joined: Sun Apr 29, 2007 8:16 am

Link error: relocation truncated to fit: R_386_16 against

Post by OniDevilkin »

So I ive taken up the challenge of OS design and am attempting to write a small protected mode OS.

So far I have a 'Hello World' Kernel that boots from grub inside a bochs environment.

Now im trying to take control of the GDT by adding in a little protected mode code.The problem is now when I link in my protected mode code I get a linker error:

pmode.o: In function `pmodeinit':
pmode.asm:(.text+0x7): relocation truncated to fit: R_386_16 against `.data'
pmode.asm:(.text+0x2d): relocation truncated to fit: R_386_16 against `.text'

Ive read the other thread regarding relocation truncated, and assume it has somthing to do with 16 vs 32bit but havent the slightest how to fix it.

Code listing follows...

Code: Select all

/* link.ld */

ENTRY (loader)

SECTIONS
{
    . = 0x00100000;

    .text :
    {
        *(.text)
    }

    .rodata ALIGN (0x1000) :
    {
        *(.rodata)
    }

    .data ALIGN (0x1000) :
    {
        *(.data)
    }

    .bss :
    {
        _sbss = .;
        *(COMMON)
        *(.bss)
        _ebss = .;
    }
}

Code: Select all

; Loader.asm

global loader           ; making entry point visible to linker
extern main             ; _main is defined elsewhere
extern pmodeinit
BITS 32

; setting up the Multiboot header - see GRUB docs for details
MODULEALIGN equ  1<<0                   ; align loaded modules on page boundaries
MEMINFO     equ  1<<1                   ; provide memory map
FLAGS       equ  MODULEALIGN | MEMINFO  ; this is the Multiboot 'flag' field
MAGIC       equ    0x1BADB002           ; 'magic number' lets bootloader find the header
CHECKSUM    equ -(MAGIC + FLAGS)        ; checksum required

section .text
align 4
MultiBootHeader:
   dd MAGIC
   dd FLAGS
   dd CHECKSUM

; reserve initial kernel stack space
STACKSIZE equ 0x4000          ; that's 16k.

loader:
   mov esp, stack+STACKSIZE           ; set up the stack
   push eax                           ; pass Multiboot magic number
   push ebx                           ; pass Multiboot info structure

   call pmodeinit
   call main                        ; call kernel proper
               hlt                    ; halt machine should kernel return

section .bss
align 32
stack:
   resb STACKSIZE      ; reserve 16k stack on a quadword boundary

Code: Select all

;------------------------------------------------------------------------------
; Filename:    pmode.asm
; Description: Takes over after grub has thrown us into protected mode and sets
;              up a flat 4GB memory space.
; Author:      Adam Scarr ([email protected])
;------------------------------------------------------------------------------

BITS 32
section .data

           ALIGN   4

GDTPtr: equ     $ + 6

GDT:

NullDes:                                ; GDT[0] = Null Descriptor
        dd      0h
        dd      0h
;------------------------------------------------------------------------------
; GDT[1] = Code Descriptor
; Limit: FFFFF                          Base: 0
; Acessed: 0                            Read-Write: 1
; Stack: 0                              Code: 1
; Desc. Privelage Level: 0              Present: 1
; User Definable: 0                     Reserved Bit: 0
; 32 bit: 1                             Granularity: 1 (4k)
; 
;------------------------------------------------------------------------------

CodeDes:
        dw      0FFFFh                  ; Limit 15-0
        dw      00000h                  ; Base 15-0
        db      00h                     ; Base 23-16
        db      10011010b               ; Flags
        db      11001111b               ; Limit 19-16 + Flags
        db      00h                     ; Base 31-24

;------------------------------------------------------------------------------
; GDT[2] = Data Descriptor 
; Limit: FFFFF                          Base: 0
; Acessed: 0                            Read-Write: 1
; Stack: 0                              Code: 0 
; Desc. Privelage Level: 0              Present: 1
; User Definable: 0                     Reserved Bit: 0
; 32 bit: 1                             Granularity: 1 (4k)
; 
;------------------------------------------------------------------------------

DataDes:
        dw      0FFFFh                  ; Limit 15-0
        dw      00000h                  ; Base 15-0
        db      00h                     ; Base 23-16
        db      10010010b               ; Flags
        db      11001111b               ; Limit 19-16 + Flags
        db      00h                     ; Base 31-24
GDTSize: equ    $ - GDT

section .text
        global pmodeinit 

pmodeinit:
        xor   eax,eax                   ; Clear EAX
        pop   eax                       ; Make a pointer to the GDT
        xor   ebx,ebx
        mov   bx,GDT
        add   eax,ebx
        mov   dword [GDTPtr+2],eax   ; And save it in our variable
        mov   word [GDTPtr],GDTSize  ; with the length of the GDT


        cli                             ; Clear interrupts, we don't do
                                        ; interrupt support for the
                                        ; protected mode
        lgdt  [GDTPtr]               ; Let the CPU know where the GDT is
        mov   eax,cr0
        or    eax,1
        mov   cr0,eax                   ; And turn protected mode bit on
        db 0eah                         ; Opcode for FAR jump
        dw Start32, 0008h       ; Address to jump to
                                        ; (0008h is the segment selector of
                                        ; this codeseg, 0008h is 0001h (entry
                                        ; number in the GDT) in bits 15-3

; 32-bit protected mode with our new GDT Descriptor loaded
Start32:

; Initialize all segment registers to 10h (entry #2 in the GDT)

           mov             ax,[CodeDes] ; entry #1 in GDT
           mov             cs,ax
           mov             ax,[DataDes]       ; entry #2 in GDT
           mov             ds,ax        ; ds = 10h
           mov             es,ax        ; es = 10h
           mov             fs,ax        ; fs = 10h
           mov             gs,ax        ; gs = 10h
           mov             ss,ax        ; ss = 10h

; Set the top of stack to allow stack operations.

           mov             esp,8000h      ; arbitrary top of stack

Code: Select all

// main.cpp
#include "video.h"

int main(void) {
        Video vid;
        vid.clear();
        vid.write("Hello World!");
}

Code: Select all

// Video.h

#ifndef VIDEO_H
#define VIDEO_H

class Video {
public:
        Video();
        ~Video();
        void clear();
        void write(char *cp);
        void put(char c);

private:
        unsigned short *videomem;
        unsigned int off;
        unsigned int pos;
};

#endif

Code: Select all

// Video.cpp

#include "video.h"

Video::Video() {
        pos = 0;
        off = 0;
        videomem = (unsigned short*)0xb8000;
}

Video::~Video() { }

void Video::clear() {
        unsigned int i;

        for(i = 0; i < (80*25); i++) {
                videomem[i] = (unsigned char) ' ' | 0x700;
        }

        pos = 0;
        off = 0;
}

void Video::write(char *cp) {
        char * str = cp, *ch;

        for(ch = str; *ch; ch++) {
                put(*ch);
        }
}

void Video::put(char c) {
        if(pos > 80) {
                pos = 0;
                off += 80;
        }

        if(off >= (80*25)) {
                clear();
        }

        videomem[off + pos] = (unsigned char) c | 0x700;
        pos++;
}
"Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius -- and a lot of courage -- to move in the opposite direction." - Einstein
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:

Post by Combuster »

what happens is that you supply a 32 bit value when it expects a 16 bit one. The offending instructions are:

mov bx,GDT
dw Start32, 0008h

the GDT is a 32-bit pointer; you can not just load it into a 16 bit register without getting complaints.
The far jump with a word start32 is just wrong. Both the operand and the opcode should be dword sized.
"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 ]
OniDevilkin
Posts: 4
Joined: Sun Apr 29, 2007 8:16 am

Post by OniDevilkin »

Whoo! Thanks for the quick reply.

You spotted the lines that were troublesome, and I think ive fixed that
mov bx,GDT
became
mov ebx,GDT

dw Start32, 0008h
became
call start32 (tho im not sure if i needed a far call, or even how to do one properly with nasm..)

And the result... my first tripplefaulting kernel... :D
"Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius -- and a lot of courage -- to move in the opposite direction." - Einstein
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:

Post by Combuster »

OniDevilkin wrote:dw Start32, 0008h
became
call start32 (tho im not sure if i needed a far call, or even how to do one properly with nasm..)

And the result... my first tripplefaulting kernel... :D
You need a far JUMP. Reason given :wink:

try using a dword far jump instead of manually emitting opcodes. The correct NASM syntax is:

Code: Select all

jmp dword 0x8:start32
Good luck with your efforts.
"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 ]
OniDevilkin
Posts: 4
Joined: Sun Apr 29, 2007 8:16 am

Post by OniDevilkin »

I found another problem... I think

Code: Select all

           mov             ax,[CodeDes] ; entry #1 in GDT
           mov             cs,ax
           mov             ax,[DataDes]       ; entry #2 in GDT
           mov             ds,ax        ; ds = 10h
           mov             es,ax        ; es = 10h
           mov             fs,ax        ; fs = 10h
           mov             gs,ax        ; gs = 10h
           mov             ss,ax        ; ss = 10h 
should be:

Code: Select all

           mov             ax,08h ; entry #1 in GDT
           mov             cs,ax
           mov             ax,10h     ; entry #2 in GDT
           mov             ds,ax        ; ds = 10h
           mov             es,ax        ; es = 10h
           mov             fs,ax        ; fs = 10h
           mov             gs,ax        ; gs = 10h
           mov             ss,ax        ; ss = 10h 
Even after changing that I still seem to be tripplefaulting as I 'mov cs, ax'. Perhaps my GDT isnt setup properly...

Heres the Bochs dump

Code: Select all

00016578107i[CPU  ] CS.d_b = 32 bit
00016578107i[CPU  ] SS.d_b = 32 bit
00016578107i[CPU  ] | EAX=00000008  EBX=0002cc80  ECX=00000001  EDX=00000001
00016578107i[CPU  ] | ESP=00106014  EBP=00067ebc  ESI=0005390d  EDI=0005390e
00016578107i[CPU  ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00016578107i[CPU  ] | SEG selector     base    limit G D
00016578107i[CPU  ] | SEG sltr(index|ti|rpl)     base    limit G D
00016578107i[CPU  ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00016578107i[CPU  ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00016578107i[CPU  ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00016578107i[CPU  ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00016578107i[CPU  ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00016578107i[CPU  ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00016578107i[CPU  ] | EIP=001001ee (001001ee)
00016578107i[CPU  ] | CR0=0x00000011 CR1=0 CR2=0x00000000
00016578107i[CPU  ] | CR3=0x00000000 CR4=0x00000000
00016578107i[CPU  ] >> mov cs, ax : 8EC8
00016578107e[CPU  ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
The only thing that confuses me now is the line that i can only assume is the violating instruction:
mov cs, ax : 8EC8
Im going on the assumption that 8EC8 is that value its trying to load into cs, which is kind of crazy, well off the GDT... But shouldnt ax be 08h?

Its just gorn 3:30am so im going to have another stab tomorrow.
"Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius -- and a lot of courage -- to move in the opposite direction." - Einstein
nick8325
Member
Member
Posts: 200
Joined: Wed Oct 18, 2006 5:49 am

Post by nick8325 »

OniDevilkin wrote:mov cs,ax
I think this is the problem. You're telling the processor to switch into 32-bit mode (by loading a 32-bit segment), but the following instructions are 16-bit! If you remove this line, then the jmp instruction that Combuster suggested will switch into 32-bit mode.
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Post by jnc100 »

OniDevilkin wrote:00016578107i[CPU ] >> mov cs, ax : 8EC8
Intel wrote:The MOV instruction cannot be used to load the CS register. Attempting to do so results in an
invalid opcode exception (#UD). To load the CS register, use the far JMP, CALL, or RET
instruction.
Oh, and 8EC8 is the opcode for the (invalid) instruction mov cs, ax. You can see the value of eax in the bochs dump (0x8) which should be correct.

Regards,
John.
OniDevilkin
Posts: 4
Joined: Sun Apr 29, 2007 8:16 am

Post by OniDevilkin »

As you can tell im still picking up asembler as im going along.

So in 32bit mode you cant access any of the 16bit registers (ax, bx...)
Instead you need to use the extended 32bit versions (eax, ebx...)

The code selector (cs) is loaded automatically for whatever segment you are jumping into, but you can still load the other segment registers (ds, ss,...).

Here is my non-tripplefaulting pmode code...

Code: Select all

;------------------------------------------------------------------------------
; Filename:    pmode.asm
; Description: Takes over after grub has thrown us into protected mode and sets
;              up a flat 4GB memory space.
; Author:      Adam Scarr ([email protected])
;------------------------------------------------------------------------------

BITS 32
section .data
extern main

           ALIGN   4

GDT:

NullDes:                                ; GDT[0] = Null Descriptor
        dd      0h
        dd      0h
;------------------------------------------------------------------------------
; GDT[1] = Code Descriptor
; Limit: FFFFF                          Base: 0
; Acessed: 0                            Read-Write: 1
; Stack: 0                              Code: 1
; Desc. Privelage Level: 0              Present: 1
; User Definable: 0                     Reserved Bit: 0
; 32 bit: 1                             Granularity: 1 (4k)
; 
;------------------------------------------------------------------------------

CodeDes:
        dw      0FFFFh                  ; Limit 15-0
        dw      00000h                  ; Base 15-0
        db      00h                     ; Base 23-16
        db      10011010b               ; Flags
        db      11001111b               ; Limit 19-16 + Flags
        db      00h                     ; Base 31-24

;------------------------------------------------------------------------------
; GDT[2] = Data Descriptor 
; Limit: FFFFF                          Base: 0
; Acessed: 0                            Read-Write: 1
; Stack: 0                              Code: 0 
; Desc. Privelage Level: 0              Present: 1
; User Definable: 0                     Reserved Bit: 0
; 32 bit: 1                             Granularity: 1 (4k)
; 
;------------------------------------------------------------------------------

DataDes:
        dw      0FFFFh                  ; Limit 15-0
        dw      00000h                  ; Base 15-0
        db      00h                     ; Base 23-16
        db      10010010b               ; Flags
        db      11001111b               ; Limit 19-16 + Flags
        db      00h                     ; Base 31-24
GDTSize: equ    $ - GDT

GDTDesc:
        dw      GDTSize - 1
        dd      GDT

section .text
        global pmodeinit 

pmodeinit:
        cli                             ; Clear interrupts, we don't do
                                        ; protected mode
        lgdt  [GDTDesc]               ; Let the CPU know where the GDT is
        mov   eax,cr0
        or    eax,1
        mov   cr0,eax                   ; And turn protected mode bit on
        jmp dword 0x8:Start32

; 32-bit protected mode with our new GDT Descriptor loaded
Start32:
; Initialize all segment registers to 10h (entry #2 in the GDT)

           mov            eax,10h       ; entry #2 in GDT
           mov            ds,ax        ; ds = 10h
           mov            es,ax        ; es = 10h
           mov            fs,ax        ; fs = 10h
           mov            gs,ax        ; gs = 10h
           mov            ss,ax        ; ss = 10h

; Set the top of stack to allow stack operations.
           sei
           mov             esp,8000h      ; arbitrary top of stack

           jmp main
"Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius -- and a lot of courage -- to move in the opposite direction." - Einstein
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Post by jnc100 »

Yeah, that code's quite similar to what I use.

A few points:

GRUB sets up protected mode with interrupts disabled so you don't need:
OniDevilkin wrote:

Code: Select all

pmodeinit:
        cli                             ; Clear interrupts, we don't do
                                        ; protected mode
 
        mov   eax,cr0
        or    eax,1
        mov   cr0,eax                   ; And turn protected mode bit on 
But you still need the lgdt instruction.

In protected mode you can still access registers like AX, AH and AL.

Apparently (someone else might know better than I) but doing

Code: Select all

mov ax, 0x10
mov ss, eax
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
actually saves you a few bytes in the output. Useful if you're writing a bootsector but not really helpful here. Something about 32-bit registers being the default in protected mode rather than the 16-bit ones.
OniDevilkin wrote:

Code: Select all

; Set the top of stack to allow stack operations.
           sei
           mov             esp,8000h      ; arbitrary top of stack 
What's 'sei' do?

And finally:
OniDevilkin wrote:

Code: Select all

jmp main
I assume main is defined in an external C module. You need to tell the linker to set 'pmodeinit' as the entry point, rather than the default 'main'. Also, gcc by default precedes all exported symbols with an underscore, so you need to actually call _main (unless you've stopped gcc doing this of course...).

Regards,
John.
urxae
Member
Member
Posts: 149
Joined: Sun Jul 30, 2006 8:16 am
Location: The Netherlands

Post by urxae »

jnc100 wrote:And finally:
OniDevilkin wrote:

Code: Select all

jmp main
I assume main is defined in an external C module. You need to tell the linker to set 'pmodeinit' as the entry point, rather than the default 'main'. Also, gcc by default precedes all exported symbols with an underscore, so you need to actually call _main (unless you've stopped gcc doing this of course...).
He's compiling to ELF (either that or his multiboot header needs some extra fields :P) so by default no underscores are prepended.
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Post by jnc100 »

urxae wrote:He's compiling to ELF (either that or his multiboot header needs some extra fields Razz) so by default no underscores are prepended.
Oh yeah. Its my fault for compiling my kernel to PE then objcopy'ing it to ELF. Silly MinGW...

Regards,
John.
User avatar
Brynet-Inc
Member
Member
Posts: 2426
Joined: Tue Oct 17, 2006 9:29 pm
Libera.chat IRC: brynet
Location: Canada
Contact:

Post by Brynet-Inc »

jnc100 wrote:Oh yeah. Its my fault for compiling my kernel to PE then objcopy'ing it to ELF. Silly MinGW...

Regards,
John.
Didn't feel like making a cross compiler... ? :wink:
Image
Twitter: @canadianbryan. Award by smcerm, I stole it. Original was larger.
Post Reply