0

I am writing a 32-bit C kernel and I tried to call a function that I wrote in assembly.
So I wrote an assembly file containing the function myfunc and then I wrote the kernel which defined myfunc as a global variable and I linked them together so I can use it in my C code.

It worked fine, but when I tried to call it, it caused a triple fault.

However, if I use inline assembly instead,

asm("call myfunc");

It does the job.

Also notice that the disassembly for asm("call myfunc"); and for myfunc(); aren't similar:
The disassembly when using myfunc():

00000000 <kmain>:
   0:   f3 0f 1e fb             endbr32 
   4:   53                      push   ebx
   5:   83 ec 08                sub    esp,0x8
   8:   e8 fc ff ff ff          call   9 <kmain+0x9>
   d:   81 c3 02 00 00 00       add    ebx,0x2
  13:   e8 fc ff ff ff          call   14 <kmain+0x14>
  18:   f4                      hlt    
  19:   83 c4 08                add    esp,0x8
  1c:   5b                      pop    ebx
  1d:   c3                      ret 

The disassembly when using asm("call myfunc");:

00000000 <kmain>:
   0:   f3 0f 1e fb             endbr32 
   4:   e8 fc ff ff ff          call   5 <kmain+0x5>
   9:   f4                      hlt    
   a:   c3                      ret

Here's how I build it:

nasm -f elf32 kernel/klink.asm -o klink.o
gcc -c kernel/main.c -m32 -nostdlib -nodefaultlibs -O1 -fno-builtin
ld -m elf_i386 -T link.ld -o KERNEL klink.o main.o
objcopy --dump-section .text=KERNEL.SYS KERNEL

link.ld:

OUTPUT_FORMAT(elf32-i386)
ENTRY(kmain)
SECTIONS
{
   . = 0x100000;
   .text : { *(.text) }
}

The kernel (main.c):

void kmain() {
   extern void myfunc();
   myfunc();                 //Here it causes a triple fault.
   //asm("call myfunc");     //But this works fine.
   asm("hlt");
}

And this is the assembly file that contains the function (klink.asm):

[BITS 32]

SECTION .text
    GLOBAL start                 ;define start as a global variable
    GLOBAL myfunc                ;also myfunc
    jmp start                    ;jump straight into start
    myfunc:
        ;idk what to write here... just some code :)
    
    EXTERN kmain                 ;define kmain as an external variable
    
    start:
        jmp kmain                ;jump into kmain
MARSHMALLOW
  • 1,315
  • 2
  • 12
  • 24
  • What [x86, I assume] processor mode is `kmain` started with? Is it 16 bit real mode, 32 bit protected mode, etc.? Are the page tables set up [and who does this]? Has the stack pointer been set up and what address? What mode is `kmain` compiled for? What mode is `myfunc` assembled for? What are the addresses that `kmain` and `myfunc` assigned. What is the disassembly for `kmain` for _both_ cases (e.g. `myfunc();` and `asm("call myfunc");`? They should be similar. – Craig Estey May 04 '21 at 22:06
  • @CraigEstey The disassembly for `myfunc()` and `asm("call myfunc")` aren't similar. – MARSHMALLOW May 04 '21 at 22:14
  • They _should_ be. `myfunc` takes _no_ arguments and returns `void`, so the code for `myfunc();` should just be `call myfunc`. If not, _that_ is a clue. Perhaps you should _edit_ your question and post the disassembly of _both_ cases in separate code blocks here. Again, what about the asm based setup code that is invoked before `kmain` is called to initialize the CPU enough so that a C program can run? That code is normally what `start` does. If the `jmp start` in `klink.asm` is _literally_ the first inst executed, again, who sets up segment registers, stack pointer, etc? – Craig Estey May 04 '21 at 22:24
  • 1
    Try using `-fno-pic` compiler option. – Jester May 04 '21 at 22:48
  • @Jester What does it mean? – MARSHMALLOW May 05 '21 at 01:40
  • 2
    `-fno-pic` or `-fno-pie` will tell GCC not to look for a GOT, and not to try to call through a PLT. `-fPIE` is the default on modern distros, and makes really clunky inefficient 32-bit code because it doesn't support RIP-relative addressing. Your disassembly omitted `-r` so you're not actually seeing the right call-target; it's probably `call __x86.get_pc_thunk.bx`. Look at `gcc -S` output, or `objdump -drwC -Mintel` if you want to disassemble your `.o` instead of linked `ld` output. – Peter Cordes May 05 '21 at 04:38

0 Answers0