I'm writing a small x86-64 kernel. I am setting up the IDT and ran into a strange error I don't quite understand. Here's a minimal example:
entry.s
extern InterruptHandler
global isr0
align 4
isr0:
jmp sharedisr
sharedisr:
push rax
push rbx
push rcx
push rdx
push rdi
push rsi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
cld
call InterruptHandler
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rsi
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
iretq
idt.cpp
#include "idt.h"
struct IDTEntry{
unsigned short offset0; //bits 0-15
unsigned short selector; //0x08 = 1000b code selector
unsigned char ist; //0
unsigned char attrib; //
unsigned short offset1; //bits 16-31
unsigned int offset2; //bits 31-63
unsigned int zero; //0
}__attribute__((packed));
struct IDTR{
unsigned short size;
unsigned long address;
}__attribute__((packed));
unsigned long isrAddresses[1];
IDTEntry idt[1];
extern "C" void InterruptHandler(){
}
void SetupIDT(){
isrAddresses[0] = {
(unsigned long)&isr0
};
IDTEntry entry;
for (unsigned int i = 0; i < 1; i++){
entry.offset0 = (unsigned short)isrAddresses[i];
entry.selector = 0x08;
entry.ist = 0;
entry.attrib = 0x8e;
entry.offset1 = (unsigned short)(isrAddresses[i] >> 16);
entry.offset2 = (unsigned int)(isrAddresses[i] >> 32);
entry.zero = 0;
idt[i] = entry;
}
unsigned long idtAddr = (unsigned long)idt;
IDTR idtr = { 4096, idtAddr };
unsigned long idtrAddr = (unsigned long)&idtr;
asm volatile("lidt (%0)" : : "r"(idtrAddr));
}
idt.h
#ifndef IDTH
#define IDTH
extern "C" void isr0(void);
void SetupIDT();
#endif
main.cpp
#include "idt.h"
void main(){
SetupIDT();
asm volatile("hlt");
}
I compile and link with this bash script:
nasm entry.s -felf64 -oentry.o
g++ -static -ffreestanding -nostdlib -mgeneral-regs-only -mno-red-zone -c -m64 main.cpp -omain.o
g++ -static -ffreestanding -nostdlib -mgeneral-regs-only -mno-red-zone -c -m64 idt.cpp -oidt.o
ld -entry main --oformat elf64-x86-64 --no-dynamic-linker -static -nostdlib -Ttext-segment=ffff800001000000 entry.o main.o idt.o -okernel.elf
I'm getting the error ld: failed to convert GOTPCREL relocation; relink with --no-relax
. If I add the --no-relax
option to ld it works and the code actually works. I can print something in the InterruptHandler function and return to the previous code which triggered the exception (I tested it works with a division by zero provoked on purpose).
From the error I guess it has something to do with linking relaxation but I don't know what it is. Can someone explain in brief what it is and why I am getting the error? Also, can anyone give any further advice on building a proper ISR. For example, should I push RBP then put RSP in RBP like what gcc does when using __attribute__((interrupt))
? Should I also have a leaveq at the end of the ISR? etc...
Thank you for any tip!