Page 1 of 1

this error with limine kernel

Posted: Sat Jul 27, 2024 10:33 am
by CesarWW
hello, i am making a OS with limine, ive attempted to add keyboard support but ran into this make file problem:

Code: Select all

ld: obj/main.cpp.o: relocation R_X86_64_32S against undefined symbol `rdi' can not be used when making a PIE object; recompile with -fPIE
ld: failed to set dynamic section sizes: bad value
make[1]: *** [GNUmakefile:125: bin/kernel] Error 1
make[1]: Leaving directory '/home/cesar/Desktop/OS/kernel'
make: *** [GNUmakefile:39: kernel] Error 2
any help appreciated

Re: this error with limine kernel

Posted: Sat Jul 27, 2024 1:31 pm
by nullplan
What this means is that somewhere you have a piece of inline assembler referencing rdi without a leading %. Can you share the code that goes into your main.cpp?

Re: this error with limine kernel

Posted: Sat Jul 27, 2024 1:40 pm
by CesarWW
code sorry for the mess (bottom has that asm code)

Code: Select all

#include <cstdint>
#include <cstddef>
#include <limine.h>
#include "utils.hpp"
#include <flanterm/flanterm.h>
#include <flanterm/backends/fb.h>
// Set the base revision to 2, this is recommended as this is the latest
// base revision described by the Limine boot protocol specification.
// See specification for further info.

namespace {

__attribute__((used, section(".requests")))
volatile LIMINE_BASE_REVISION(2);

}

// The Limine requests can be placed anywhere, but it is important that
// the compiler does not optimise them away, so, usually, they should
// be made volatile or equivalent, _and_ they should be accessed at least
// once or marked as used with the "used" attribute as done here.

namespace {

__attribute__((used, section(".requests")))
volatile limine_framebuffer_request framebuffer_request = {
    .id = LIMINE_FRAMEBUFFER_REQUEST,
    .revision = 0,
    .response = nullptr
};

}

// Finally, define the start and end markers for the Limine requests.
// These can also be moved anywhere, to any .cpp file, as seen fit.

namespace {

__attribute__((used, section(".requests_start_marker")))
volatile LIMINE_REQUESTS_START_MARKER;

__attribute__((used, section(".requests_end_marker")))
volatile LIMINE_REQUESTS_END_MARKER;

}



// Halt and catch fire function.
namespace {

void hcf() {
    asm ("cli");
    for (;;) {
        asm ("hlt");
    }
}

}

// Outb and Inb functions
static inline void outb(uint16_t port, uint8_t value) {
    asm volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
}

static inline uint8_t inb(uint16_t port) {
    uint8_t ret;
    asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
    return ret;
}

// IDT entry
struct idt_entry {
    uint16_t offset_low;
    uint16_t selector;
    uint8_t zero;
    uint8_t type_attr;
    uint16_t offset_mid;
    uint32_t offset_high;
    uint32_t zero2;
} __attribute__((packed));

// IDT pointer
struct idt_ptr {
    uint16_t limit;
    uint64_t base;
} __attribute__((packed));

static struct idt_entry idt[256];
static struct idt_ptr idtp;


void set_idt_gate(int n, uint64_t handler) {
    idt[n].offset_low = handler & 0xFFFF;
    idt[n].selector = 0x08; // Kernel code segment
    idt[n].zero = 0;
    idt[n].type_attr = 0x8E; // Interrupt gate
    idt[n].offset_mid = (handler >> 16) & 0xFFFF;
    idt[n].offset_high = (handler >> 32) & 0xFFFFFFFF;
    idt[n].zero2 = 0;
}

extern "C" void load_idt(struct idt_ptr* idt_ptr);
extern "C" void keyboard_isr(void);

void init_idt() {
    idtp.limit = sizeof(idt) - 1;
    idtp.base = (uint64_t)&idt;
    load_idt(&idtp);
}

void pic_remap() {
    outb(0x20, 0x11);
    outb(0xA0, 0x11);
    outb(0x21, 0x20);
    outb(0xA1, 0x28);
    outb(0x21, 0x04);
    outb(0xA1, 0x02);
    outb(0x21, 0x01);
    outb(0xA1, 0x01);
    outb(0x21, 0x0);
    outb(0xA1, 0x0);
}

struct flanterm_context *ft_ctx;

// The following stubs are required by the Itanium C++ ABI (the one we use,
// regardless of the "Itanium" nomenclature).
// Like the memory functions above, these stubs can be moved to a different .cpp file,
// but should not be removed, unless you know what you are doing.
extern "C" {
    int __cxa_atexit(void (*)(void *), void *, void *) { return 0; }
    void __cxa_pure_virtual() { hcf(); }
    void *__dso_handle;
}
void __fb_init(struct limine_framebuffer_response *fb_response) {
    struct limine_framebuffer *fb = fb_response->framebuffers[0];
    uint32_t fg = 0x000000;
    uint32_t bg = 0xffffff;

  

    ft_ctx = flanterm_fb_init(NULL, NULL, (uint32_t*)fb->address, fb->width, fb->height, fb->pitch, fb->red_mask_size, fb->red_mask_shift, fb->green_mask_size, fb->green_mask_shift, fb->blue_mask_size, fb->blue_mask_shift, NULL, NULL, NULL, &bg, &fg, NULL, NULL, NULL, 8, 16, 1, 1, 1, 5);
    
}
void print(const char msg[]){
 
    
    flanterm_write(ft_ctx, msg, sizeof(msg));

}
extern "C" void keyboard_handler() {
    uint8_t scancode = inb(0x60);

    char msg[] = "Key pressed: 0x00\n";
    const char *hex = "0123456789ABCDEF";
    msg[14] = hex[(scancode >> 4) & 0xF];
    msg[15] = hex[scancode & 0xF];
    print(msg);

    // End of interrupt
    outb(0x20, 0x20);
}

// Extern declarations for global constructors array.
extern void (*__init_array[])();
extern void (*__init_array_end[])();

// The following will be our kernel's entry point.
// If renaming _start() to something else, make sure to change the
// linker script accordingly.
extern "C" void _start() {
    // Ensure the bootloader actually understands our base revision (see spec).
    if (LIMINE_BASE_REVISION_SUPPORTED == false) {
        hcf();
    }

    // Call global constructors.
    for (std::size_t i = 0; &__init_array[i] != __init_array_end; i++) {
        __init_array[i]();
    }

    // Ensure we got a framebuffer.
    if (framebuffer_request.response == nullptr
     || framebuffer_request.response->framebuffer_count < 1) {
        hcf();
    }
    if (framebuffer_request.response->framebuffers[0] != NULL){
        __fb_init(framebuffer_request.response);
    }


 
    print("test");
    init_idt();
    pic_remap();
   set_idt_gate(33, (uint64_t)keyboard_isr);
    asm volatile("sti"); // Enable interrupts

    
   

    // We're done, just hang...
    hcf();
}
// Assembly stubs
__asm__(
".global load_idt\n"
"load_idt:\n"
"    lidt [rdi]\n"
"    ret\n"
".global keyboard_isr\n"
"keyboard_isr:\n"
"    cli\n"
"    push rax\n"
"    push rbx\n"
"    push rcx\n"
"    push rdx\n"
"    push rsi\n"
"    push rdi\n"
"    push r8\n"
"    push r9\n"
"    push r10\n"
"    push r11\n"
"    push r12\n"
"    push r13\n"
"    push r14\n"
"    push r15\n"
"    call keyboard_handler\n"
"    pop r15\n"
"    pop r14\n"
"    pop r13\n"
"    pop r12\n"
"    pop r11\n"
"    pop r10\n"
"    pop r9\n"
"    pop r8\n"
"    pop rdi\n"
"    pop rsi\n"
"    pop rdx\n"
"    pop rcx\n"
"    pop rbx\n"
"    pop rax\n"
"    sti\n"
"    iretq\n"
);


Re: this error with limine kernel

Posted: Sat Jul 27, 2024 5:16 pm
by Octocontrabass
The inline asm at the bottom of the file appears to be Intel syntax, but the inline asm elsewhere is AT&T syntax. You can't mix the two, you have to pick one syntax for the entire compilation unit.

It looks like that code was originally in a separate file. Is there any particular reason why you didn't leave it in a separate file?

Re: this error with limine kernel

Posted: Sun Jul 28, 2024 3:46 am
by CesarWW
it was never in a seperate file, just added it like this
im really new to all of this so would you mind just explainin to me or show how i can convect to asm to any of the 2 platforms you mentioned?

Re: this error with limine kernel

Posted: Sun Jul 28, 2024 9:09 am
by nullplan
Basically, you reverse the operand order, prefix all registers with %, and change the [] notation to () notation. Since you aren't using any of the complicated cases, this essentially just means

Code: Select all

__asm__(
".global load_idt\n"
"load_idt:\n"
"    lidt (%rdi)\n"
"    ret\n"
".global keyboard_isr\n"
"keyboard_isr:\n"
"    push %rax\n"
"    push %rbx\n"
"    push %rcx\n"
"    push %rdx\n"
"    push %rsi\n"
"    push %rdi\n"
"    push %r8\n"
"    push %r9\n"
"    push %r10\n"
"    push %r11\n"
"    push %r12\n"
"    push %r13\n"
"    push %r14\n"
"    push %r15\n"
"    call keyboard_handler\n"
"    pop %r15\n"
"    pop %r14\n"
"    pop %r13\n"
"    pop %r12\n"
"    pop %r11\n"
"    pop %r10\n"
"    pop %r9\n"
"    pop %r8\n"
"    pop %rdi\n"
"    pop %rsi\n"
"    pop %rdx\n"
"    pop %rcx\n"
"    pop %rbx\n"
"    pop %rax\n"
"    iretq\n"
);
I also got rid of the spurious sti and cli in keyboard_isr for you. The cli is superfluous if you are using an interrupt gate for the keyboard ISR (and you should always be using those - I have so far not found a sensible use for trap gates). And the sti just before iret is useless, because part of iret's operation is to pop flags, which overwrites the interrupt flag with the value from the interrupt frame. Also, if the sti wasn't useless, and an interrupt could trigger there, this could lead to a stack overflow.

Re: this error with limine kernel

Posted: Sun Jul 28, 2024 3:11 pm
by CesarWW
this has the os booting. but when running the keyboard_handler function in a while loop, it just shows key pressed as 0FA0 even when not pressing anything. my updated code is the following:

Code: Select all

#include <cstdint>
#include <cstddef>

#include <limine.h>
#include "utils.hpp"
#include <flanterm/flanterm.h>
#include <flanterm/backends/fb.h>
// Set the base revision to 2, this is recommended as this is the latest
// base revision described by the Limine boot protocol specification.
// See specification for further info.

namespace {

__attribute__((used, section(".requests")))
volatile LIMINE_BASE_REVISION(2);

}

// The Limine requests can be placed anywhere, but it is important that
// the compiler does not optimise them away, so, usually, they should
// be made volatile or equivalent, _and_ they should be accessed at least
// once or marked as used with the "used" attribute as done here.

namespace {

__attribute__((used, section(".requests")))
volatile limine_framebuffer_request framebuffer_request = {
    .id = LIMINE_FRAMEBUFFER_REQUEST,
    .revision = 0,
    .response = nullptr
};

}

// Finally, define the start and end markers for the Limine requests.
// These can also be moved anywhere, to any .cpp file, as seen fit.

namespace {

__attribute__((used, section(".requests_start_marker")))
volatile LIMINE_REQUESTS_START_MARKER;

__attribute__((used, section(".requests_end_marker")))
volatile LIMINE_REQUESTS_END_MARKER;

}



// Halt and catch fire function.
namespace {

void hcf() {
    asm ("cli");
    for (;;) {
        asm ("hlt");
    }
}

}
std::size_t strlen(const char* str) {
    const char* s = str;
    while (*s) {
        ++s;
    }
    return s - str;
}
// Outb and Inb functions
static inline void outb(uint16_t port, uint8_t value) {
    asm volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
}

static inline uint8_t inb(uint16_t port) {
    uint8_t ret;
    asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
    return ret;
}

// IDT entry
struct idt_entry {
    uint16_t offset_low;
    uint16_t selector;
    uint8_t zero;
    uint8_t type_attr;
    uint16_t offset_mid;
    uint32_t offset_high;
    uint32_t zero2;
} __attribute__((packed));

// IDT pointers
struct idt_ptr {
    uint16_t limit;
    uint64_t base;
} __attribute__((packed));

static struct idt_entry idt[256];
static struct idt_ptr idtp;


bool set_idt_gate(int n, uint64_t handler) {
   // try{
        idt[n].offset_low = handler & 0xFFFF;
        idt[n].selector = 0x08; // Kernel code segment
        idt[n].zero = 0;
        idt[n].type_attr = 0x8E; // Interrupt gate
        idt[n].offset_mid = (handler >> 16) & 0xFFFF;
        idt[n].offset_high = (handler >> 32) & 0xFFFFFFFF;
        idt[n].zero2 = 0;
        return true;
  //  }
   // catch(...){
   //     return false;
   // }
}

extern "C" void load_idt(struct idt_ptr* idt_ptr);
extern "C" void keyboard_isr(void);

bool init_idt() {
   // try{
        idtp.limit = sizeof(idt) - 1;
        idtp.base = (uint64_t)&idt;
        load_idt(&idtp);
        return true;
   // }
  //  catch(...){
  //      return false;
  //  }
   
}

bool pic_remap() {
  //  try{
        outb(0x20, 0x11);
        outb(0xA0, 0x11);
        outb(0x21, 0x20);
        outb(0xA1, 0x28);
        outb(0x21, 0x04);
        outb(0xA1, 0x02);
        outb(0x21, 0x01);
        outb(0xA1, 0x01);
        outb(0x21, 0x0);
        outb(0xA1, 0x0);
        return true;
  //  }
  //  catch(...){
  //      return false;
  //  }
}

struct flanterm_context *ft_ctx;

// The following stubs are required by the Itanium C++ ABI (the one we use,
// regardless of the "Itanium" nomenclature).
// Like the memory functions above, these stubs can be moved to a different .cpp file,
// but should not be removed, unless you know what you are doing.
extern "C" {
    int __cxa_atexit(void (*)(void *), void *, void *) { return 0; }
    void __cxa_pure_virtual() { hcf(); }
    void *__dso_handle;
}
void __fb_init(struct limine_framebuffer_response *fb_response) {
    struct limine_framebuffer *fb = fb_response->framebuffers[0];
    uint32_t fg = 0x000000;
    uint32_t bg = 0xffffff;

  

    ft_ctx = flanterm_fb_init(NULL, NULL, (uint32_t*)fb->address, fb->width, fb->height, fb->pitch, fb->red_mask_size, fb->red_mask_shift, fb->green_mask_size, fb->green_mask_shift, fb->blue_mask_size, fb->blue_mask_shift, NULL, NULL, NULL, &bg, &fg, NULL, NULL, NULL, 8, 16, 1, 1, 1, 5);
    
}
void print(const char msg[]){
 
    std::size_t length = strlen(msg);
    flanterm_write(ft_ctx, msg, length);

}
void clear_screen() {
    const char* clear_sequence = "\x1b[2J\x1b[H";
    std::size_t length = strlen(clear_sequence);
    flanterm_write(ft_ctx, clear_sequence, length);
}
extern "C" void keyboard_handler() {
    uint8_t scancode = inb(0x60);

    char msg[] = "Key pressed: 0x00\n";
    const char *hex = "0123456789ABCDEF";
    msg[14] = hex[(scancode >> 4) & 0xF];
    msg[15] = hex[scancode & 0xF];
    print(msg);

    // End of interrupt
    outb(0x20, 0x20);
}

extern "C" void _secondKernelPhase(){

    hcf();

}
 
// Extern declarations for global constructors array.
extern void (*__init_array[])();
extern void (*__init_array_end[])();

// The following will be our kernel's entry point.
// If renaming _start() to something else, make sure to change the
// linker script accordingly.
extern "C" void _start() {
    // Ensure the bootloader actually understands our base revision (see spec).
    if (LIMINE_BASE_REVISION_SUPPORTED == false) {
        hcf();
    }

    // Call global constructors.
    for (std::size_t i = 0; &__init_array[i] != __init_array_end; i++) {
        __init_array[i]();
    }

    // Ensure we got a framebuffer.
    if (framebuffer_request.response == nullptr
     || framebuffer_request.response->framebuffer_count < 1) {
        hcf();
    }
    if (framebuffer_request.response->framebuffers[0] != NULL){
        __fb_init(framebuffer_request.response);
    }

    print("cOS First Phase Kernel: Initializing... ");
    init_idt();
    pic_remap();
    set_idt_gate(33, (uint64_t)keyboard_isr);
    asm volatile("sti");
    while(true){
        keyboard_handler();
        
    }
    hcf();

    
   
}

__asm__(
".global load_idt\n"
"load_idt:\n"
"    lidt (%rdi)\n"
"    ret\n"
".global keyboard_isr\n"
"keyboard_isr:\n"
"    push %rax\n"
"    push %rbx\n"
"    push %rcx\n"
"    push %rdx\n"
"    push %rsi\n"
"    push %rdi\n"
"    push %r8\n"
"    push %r9\n"
"    push %r10\n"
"    push %r11\n"
"    push %r12\n"
"    push %r13\n"
"    push %r14\n"
"    push %r15\n"
"    call keyboard_handler\n"
"    pop %r15\n"
"    pop %r14\n"
"    pop %r13\n"
"    pop %r12\n"
"    pop %r11\n"
"    pop %r10\n"
"    pop %r9\n"
"    pop %r8\n"
"    pop %rdi\n"
"    pop %rsi\n"
"    pop %rdx\n"
"    pop %rcx\n"
"    pop %rbx\n"
"    pop %rax\n"
"    iretq\n"
);


Re: this error with limine kernel

Posted: Mon Jul 29, 2024 1:40 am
by nullplan
keyboard_handler is an interrupt handler. If no interrupt occurs, it won't work. It will also only work with PS/2 keyboards, so if your VM emulates something else, you will have problems.

For now, why don't you just get rid of the cli in hcf(), or even replace it with an sti? Then after initialization, the kernel will just execute the hlt instruction in a loop with the interrupt flag set, so it will just continuously wait for interrupts. That way, you should be able to see if your interrupt handler works right.