LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Problem linking a C script with inline assembly to a ASM file. (https://www.linuxquestions.org/questions/programming-9/problem-linking-a-c-script-with-inline-assembly-to-a-asm-file-4175610753/)

TheStr3ak5 07-27-2017 03:07 PM

Problem linking a C script with inline assembly to a ASM file.
 
Hi, lets get into the problem:
I were compiling my prototype of prototype of a kernel (sounds weird, but it really doesnt matter) and in the instalation I need to link the ASM file to a C file compiled with gcc to get a executable that could be used as kernel.
The problem is that, after implementing a swap to protected mode from real mode, I get this error at linking the kernel.c and loader.asm scripts:

Code:

kernel.c:(.text+0x1e1): undefined reference to `gdtr'
I will explain how all process of instalation is and I will put the codes below.
Instalation steps:
1: Compile asm:
Code:

nasm -f elf32 loader.asm -o kasm.o
2: Compile .c :
Code:

gcc -m32 -ffreestanding -c kernel.c -o kc.o
3: Link both:
Code:

ld -m elf_i386 -T linker.ld -o kernel kasm.o kc.o
The complete error output is:
Code:

kc.o: In function `k_enter_protected_mode':
kernel.c:(.text+0x1e1): undefined reference to `gdtr'

The code looks like:
Code:

/*
 *
 * 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
 *
 */
/* Output defines */
#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*/
#define CYAN_TXT 0x03 /*light cyan on black text */
#include <stddef.h>
#include <stdint.h>
#include <cpuid.h>
void k_clear_screen();
void k_sleep_3sec();
unsigned int k_printf(char *message, unsigned int line, float color);
void k_malloc(size_t sz);
void k_free(void *mem);
/* 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_3sec : to make a simple delay of aprox 3 sec, since is a nasty sleep,
* duration will vary
* from system to system
*/
void k_sleep_3sec()
{
        int c = 1, d = 1;
        for ( c = 1 ; c <= 20000 ; c++ )
    for ( d = 1 ; d <= 20000 ; d++ )
    {}
}
/*
* Malloc and free functions for this kernel
* Maybe change in the future, sure
*/
static unsigned char our_memory[1024 * 1024]; /* reserve 1 MB for malloc */
static size_t next_index = 0;
int k_malloc_err;
void k_malloc(size_t sz)
{
        void *mem;
        if(sizeof our_memory - next_index < sz){
            return NULL;
        k_malloc_err = 1;
    }

    mem = &our_memory[next_index];
    next_index += sz;
    return mem;
}
void k_free(void *mem)
{
  /* we cheat, and don't free anything. */
}
/* Schreduler */
/*---*/
/*
* Our schreduler is a RTC (Run to Completion)
* In the future we will add more schredulers or change the type
* but for now this is what we got
*/
int proc_number_count = 0;
void k_schreduler(char *proc_name, unsigned int proc_prior)
{
        proc_number_count = proc_number_count + 1;
        int proc_number = proc_number_count;
}
void k_enter_protected_mode()
{
    __asm__ volatile ("cli;"         
        "lgdt (gdtr);" 
        "mov %eax, cr0;"
        "or %al, 1;"   
        "mov cr0, %eax;"
        "jmp 0x8,PModeMain;"
        "PModeMain:");
}
/*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, WHITE_TXT);
        k_printf(" =-=-=-=-=-=-=-=-=-=-=-=-=-=-", 1, WHITE_TXT);
        k_printf("[KernelInfo] Woah! No Kernel Panic for now! Well, lets fix that...", 2, CYAN_TXT);
        k_printf("[Proc1] Checking for k_malloc() and k_free() kernel functions", 3, WHITE_TXT);
        k_malloc(15);
        if (k_malloc_err == 1){
                k_printf("[F-ERROR] Unable to use k_malloc, do you have enough memory?", 4, RED_TXT);
                while(1){
                        int error_stayer = 1;
                }

        } else{
                k_printf("[Proc1] k_malloc and k_free found, resuming boot...", 4, GREEN_TXT);
        }
        k_enter_protected_mode();
        k_printf("[KernelInfo] Switched to protected mode successfully", 5, CYAN_TXT);

}

This was kernel.c
---
Code:

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 = .;
  }
}

This was the linker.ld
---
Code:

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

This was loader.asm
---
I tried to solve this transforming the ASM block in an advanced ASM block And parsing gdtr as an argument but I dont understand this last method
How can I solve the error? Thanks in advance!

NevemTeve 07-28-2017 12:28 PM

Maybe it should be %gdtr as it is a register.

TheStr3ak5 07-28-2017 01:56 PM

Quote:

Originally Posted by NevemTeve (Post 5741098)
Maybe it should be %gdtr as it is a register.

Doing this leaves more errors:
Doing what you sugested will change the block from the original to:
Code:

void k_enter_protected_mode()
{
    __asm__ volatile ("cli;"         
        "lgdt (%gdtr);" 
        "mov %eax, cr0;"
        "or %al, 1;"   
        "mov cr0, %eax;"
        "jmp 0x8,PModeMain;"
        "PModeMain:");
}

And at compiling in gcc i get this errors:
Code:

kernel.c: Assembler messages:
kernel.c:118: Error: bad expression
kernel.c:118: Error: found 'g', expected: ')'
kernel.c:118: Error: junk `gdtr)' after expression


ntubski 07-28-2017 02:35 PM

AFAICT, the argument to lgdt is a memory address, not a register.

https://en.wikibooks.org/wiki/X86_As...scriptor_Table
Quote:

To load the GDTR, the instruction LGDT is used:

Code:

lgdt [gdtr]
Where gdtr is a pointer to 6 bytes of memory containing the desired GDTR value. Note that to complete the process of loading a new GDT, the segment registers need to be reloaded.
So you need to define gdtr to point to some memory.

Fat_Elvis 08-09-2017 10:35 AM

That inline asm looks very odd.

The prefixes are mandatory:

/* Note it's %%eax, instead of %eax in C source files. */

(%%eax) is a pointer.
%%eax is a register.
$1 is an immediate value.

At the end of the block you have:

: /* Outputs */
: /* Inputs */
: /* Clobbered registers */

Maybe check the GCC manual.

pan64 08-09-2017 12:25 PM

I cannot compile this kernel.c:
Code:

$ gcc -m32 -ffreestanding -c kernel.c -o kc.o
kernel.c: In function ‘k_malloc’:
kernel.c:91:16: warning: ‘return’ with a value, in function returning void
        return NULL;
                ^~~~
kernel.c:87:6: note: declared here
 void k_malloc(size_t sz)
      ^~~~~~~~
kernel.c:97:12: warning: ‘return’ with a value, in function returning void
    return mem;
            ^~~
kernel.c:87:6: note: declared here
 void k_malloc(size_t sz)
      ^~~~~~~~
kernel.c: Assembler messages:
kernel.c:118: Error: too many memory references for `jmp'
$

which gcc did you try?

gdtr is not defined at all, so undefined reference is ok.


All times are GMT -5. The time now is 12:58 PM.