1

i will explain my problem...
I'm coding a kernel in c + assembly, and when I coded some asm in-line in the main c script with gcc as compiler I had this problem:
All looked fine, the script compiled as always, without errors or warnings, but when I tried to build the version of my program (the kernel I was building) had a linker problem, pointing to a script failure.
The installation is simple, it is like:
Step 1: nasm -f elf32 loader.asm -o kasm.o
Step 2: gcc -m32 -c kernel.c -o kc.o
Step 3: ld -m elf_i386 -T linker.ld -o kernel kasm.o kc.o
And you get a executable than can be run at qemu as kernel.
But when it came to the last step (the step 3) I had this problem:

kc.o: In function `enablePaging':
kernel.c:(.text+0x379): undefined reference to `__stack_chk_fail_local'
ld: kernel: hidden symbol `__stack_chk_fail_local' isn't defined
ld: final link failed: Bad value

I put here the 3 scripts involved in this process:
1: kernel.c, the main kernel file

/*
 *
 * kernel.c - version 0.0.1
 * This script is under the license of the distributed package, this license
 * can be found in the package itself
 * Script coded by Cristian Simón for the CKA Proyect
 * ----
 * License: GNU GPL v3
 * Coder: Cristian Simón
 * Proyect: CKA 
 * 
 */

#define BLACK_BGROUND 0X07 /* black background */
#define WHITE_TXT 0x07 /* light gray on black text */
#define GREEN_TXT 0x02 /* light green on black text */
#define RED_TXT 0x04 /* light red on black text*/
#include <stddef.h>
#include <stdint.h>

void k_clear_screen();
void k_sleep_3sec();
unsigned int k_printf(char *message, unsigned int line, float color);

/* k_clear_screen : to clear the entire text screen */
void k_clear_screen()
{
    char *vidmem = (char *) 0xC00B8000;
    unsigned int i=0;
    while(i < (80*25*2))
    {
        vidmem[i]=' ';
        i++;
        vidmem[i]=BLACK_BGROUND;
        i++;
    };
};

/* k_printf : the message and the line # */
unsigned int k_printf(char *message, unsigned int line, float color)
{
    char *vidmem = (char *) 0xC00B8000;
    unsigned int i=0;

    i=(line*80*2);

    while(*message!=0)
    {
        if(*message=='\n') // check for a new line
        {
            line++;
            i=(line*80*2);
            *message++;
        } else {
            vidmem[i]=*message;
            *message++;
            i++;
            vidmem[i]=color;
            i++;
        };
    };

    return(1);
}

/* k_sleep_10sec : to make a simple delay of aprox 10 sec */
void k_sleep_3sec()
{
    int c = 1, d = 1;
    for ( c = 1 ; c <= 20000 ; c++ )
    for ( d = 1 ; d <= 20000 ; d++ )
    {}
}  
extern void loadPageDirectory(unsigned int*);
extern void enablePaging();
/*main function*/
void k_main() 
{
    k_clear_screen();
    k_printf(" Wellcome to", 0, WHITE_TXT);
    k_printf(" CKA!", 1, GREEN_TXT);
    k_printf("==============>", 2, WHITE_TXT);
    k_printf(" CKA stands for C Kernel with Assembly", 3, WHITE_TXT);
    k_printf(" Version 0.0.1, => based in the job of Debashis Barman", 4, WHITE_TXT);
    k_printf(" Contact => assemblyislaw@gmail.com / blueshell@mail2tor.com", 5, WHITE_TXT);
    k_printf("           or in the github repository page", 6, WHITE_TXT);
    k_sleep_3sec();
    k_clear_screen();
    //here start the magic
    k_printf(" Starting Checkup", 0, RED_TXT);

    //paging setup
    k_printf(" [Proc1] Setting up paging", 1, WHITE_TXT);
    uint32_t page_directory[1024] __attribute__((aligned(4096)));
    //set each entry to not present
    int i;
    for(i = 0; i < 1024; i++)
    {
        // This sets the following flags to the pages:
        //   Supervisor: Only kernel-mode can access them
        //   Write Enabled: It can be both read from and written to
        //   Not Present: The page table is not present
        page_directory[i] = 0x00000002;
    }
    uint32_t first_page_table[1024] __attribute__((aligned(4096)));
    // holds the physical address where we want to start mapping these pages to.
    // in this case, we want to map these pages to the very beginning of memory.
    unsigned int w;

    //we will fill all 1024 entries in the table, mapping 4 megabytes
    for(w = 0; w < 1024; w++)
    {
        // As the address is page aligned, it will always leave 12 bits zeroed.
        // Those bits are used by the attributes ;)
        first_page_table[w] = (w * 0x1000) | 3; // attributes: supervisor level, read/write, present.
    }
    // attributes: supervisor level, read/write, present
    page_directory[0] = ((unsigned int)first_page_table) | 3;
    //paging asm
    asm(".text\n\t"
            ".globl loadPageDirectory\n\t" 
            "loadPageDirectory:\n\t" 
            "push %ebp\n\t"
            "mov %esp, %ebp\n\t"
            "mov 8(%esp), %eax\n\t"
            "mov %eax, %cr3\n\t"
            "mov %ebp, %esp\n\t"
            "pop %ebp\n\t"
            "ret"
    );
    asm(".text\n\t"
            ".globl enablePaging\n\t"
            "enablePaging:\n\t"
            "push %ebp\n\t"
            "mov %esp, %ebp\n\t"
            "mov %cr0, %eax\n\t"
            "or $0x80000000, %eax\n\t"
            "mov %eax, %cr0\n\t"
            "mov %ebp, %esp\n\t"
            "pop %ebp\n\t"
            "ret"
    );
    loadPageDirectory(page_directory);
    enablePaging();
    k_printf("[Proc1] Paging enabled!", 2, GREEN_TXT);
}

As you can see the asm code that is my suspect of cause the problem is at the end, in the second asm(...) block

Script 2: linker, to link loader and kernel

ENTRY(loader)
OUTPUT_FORMAT(elf32-i386)

SECTIONS {
   /* The kernel will live at 3GB + 1MB in the virtual
      address space, which will be mapped to 1MB in the
      physical address space. */
   . = 0xC0100000;

   .text : AT(ADDR(.text) - 0xC0000000) {
       *(.text)
       *(.rodata*)
   }

   .data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) {
       *(.data)
   }

   .bss : AT(ADDR(.bss) - 0xC0000000) {
       _sbss = .;
       *(COMMON)
       *(.bss)
       _ebss = .;
   }
}

Script 3: Loader, in assembly.

global _loader                          ; Make entry point visible to linker.
extern k_main                           ; _main is defined elsewhere

; 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

; This is the virtual base address of kernel space. It must be used to convert virtual
; addresses into physical addresses until paging is enabled. Note that this is not
; the virtual address where the kernel image itself is loaded -- just the amount that must
; be subtracted from a virtual address to get a physical address.
KERNEL_VIRTUAL_BASE equ 0xC0000000                  ; 3GB
KERNEL_PAGE_NUMBER equ (KERNEL_VIRTUAL_BASE >> 22)  ; Page directory index of kernel's 4MB PTE.


section .data
align 0x1000
BootPageDirectory:
    ; This page directory entry identity-maps the first 4MB of the 32-bit physical address space.
    ; All bits are clear except the following:
    ; bit 7: PS The kernel page is 4MB.
    ; bit 1: RW The kernel page is read/write.
    ; bit 0: P  The kernel page is present.
    ; This entry must be here -- otherwise the kernel will crash immediately after paging is
    ; enabled because it can't fetch the next instruction! It's ok to unmap this page later.
    dd 0x00000083
    times (KERNEL_PAGE_NUMBER - 1) dd 0                 ; Pages before kernel space.
    ; This page directory entry defines a 4MB page containing the kernel.
    dd 0x00000083
    times (1024 - KERNEL_PAGE_NUMBER - 1) dd 0  ; Pages after the kernel image.


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

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

; setting up entry point for linker
loader equ (_loader - 0xC0000000)
global loader

_loader:
    ; NOTE: Until paging is set up, the code must be position-independent and use physical
    ; addresses, not virtual ones!
    mov ecx, (BootPageDirectory - KERNEL_VIRTUAL_BASE)
    mov cr3, ecx                                        ; Load Page Directory Base Register.

    mov ecx, cr4
    or ecx, 0x00000010                          ; Set PSE bit in CR4 to enable 4MB pages.
    mov cr4, ecx

    mov ecx, cr0
    or ecx, 0x80000000                          ; Set PG bit in CR0 to enable paging.
    mov cr0, ecx

    ; Start fetching instructions in kernel space.
    ; Since eip at this point holds the physical address of this command (approximately 0x00100000)
    ; we need to do a long jump to the correct virtual address of StartInHigherHalf which is
    ; approximately 0xC0100000.
    lea ecx, [StartInHigherHalf]
    jmp ecx                                                     ; NOTE: Must be absolute jump!

StartInHigherHalf:
    ; Unmap the identity-mapped first 4MB of physical address space. It should not be needed
    ; anymore.
    mov dword [BootPageDirectory], 0
    invlpg [0]

    ; NOTE: From now on, paging should be enabled. The first 4MB of physical address space is
    ; mapped starting at KERNEL_VIRTUAL_BASE. Everything is linked to this address, so no more
    ; position-independent code or funny business with virtual-to-physical address translation
    ; should be necessary. We now have a higher-half kernel.
    mov esp, stack+STACKSIZE           ; set up the stack
    push eax                           ; pass Multiboot magic number

    ; pass Multiboot info structure -- WARNING: This is a physical address and may not be
    ; in the first 4MB!
    push ebx

    call  k_main                 ; call kernel proper
    hlt                          ; halt machine should kernel return


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

Honestly I dont think the last one have nothing wrong, but is Freestanding coding with a language that I dont domine, so well, ensure is nothing bad at all.
Some specs of the kernel that could be helpfull:
The part that does error is related to paging.
It works in "Higher Half", but I dont thing this disturb anything.

Rottenheimer2
  • 425
  • 1
  • 5
  • 13
  • What happens if you compile with `gcc -m32 -ffreestanding -c kernel.c -o kc.o` – Michael Petch Jul 22 '17 at 22:08
  • I'll gather you are on a version of Ubuntu that is likely 16.04 or higher (or something based on it) or a newer version of Debian (ie Debian 9) or something based on it? – Michael Petch Jul 22 '17 at 22:10
  • @MichaelPetch The problem of the linker disapears but the system, when i try to run it, starts something like a while loop with no end in the k_main function, I would recomend you to try it if possible, but I think it is problem of the paging, not of the linker work or gcc – Rottenheimer2 Jul 22 '17 at 22:12
  • @MichaelPetch is running in Ubuntu higher than 16.04, 17.04 – Rottenheimer2 Jul 22 '17 at 22:14
  • Your question suggested that you were getting the linker error `kernel.c:(.text+0x379): undefined reference to `__stack_chk_fail_local'` . So did my suggestion fix that error, but you are now having a new problem running the code now? – Michael Petch Jul 22 '17 at 22:15
  • @MichaelPetch exactly, i were having that problem and now I have other with the code, maybe is the paging or something related to memory, my experience debugging my own programs says that... – Rottenheimer2 Jul 22 '17 at 22:19
  • I don't have time to run it and I do think there is a problem with your page tables, but before even looking at those - the two _asm_ blocks inside the `k_main` function should be moved outside. That code will be dumped right in the middle of the function which is not what you want. As well `first_page_table` and `page_directory` structures you have placed inside a function - they will go on the stack. Your stack will likely be too small (16k) for the tables plus alignment. Those two variables should be moved outside the function so they will be allocated in the `.bss` section. – Michael Petch Jul 22 '17 at 23:58
  • You only identity map the first 4mb. You forgot to map the higher half addresses which would require adding an entry to at least map the 4mb from 0xc0000000 for your kernel. You'd do this by creating another page table (just like first_page_table and setting page_directory[768] to it. – Michael Petch Jul 23 '17 at 12:35

0 Answers0