Interfacing C functions with asm code at booting level? - c

I am following a tutorial about creating a 32bit operating system. I have managed to switch to the 32bit protected mode happily but could not get further.
Below, boot_sect.asm is the main assembly code. After switching to protected mode inside boot_sect.asm, an external C function (kmain) is called via kernel_entry.asm (calls the c function) from boot_sect.asm. The location of the C function in the ram is 0x1000000.
The kmain() is supposed to print an x on the screen. However, this does not happen. Indeed, to diagnose the code, I put an infinite while loop inside the kmain(), but the program did not hang there; instead it directly ended. I guess the files are not linked properly or there is a memory overlap problem. I could not get any further, and I am stuck at this point.
I would appreciate any idea or help to get over this step.
boot_sect.asm (the main asm code)
; A boot sector that boots a C kernel in 32-bit protected mode
[bits 16]
[org 0x7c00]
KERNEL_OFFSET equ 0x100000 ; This is the memory offset to where kernel will be loaded
mov [BOOT_DRIVE], dl
mov bp, 0x9000 ; stack origin.
mov sp, bp
mov bx, MSG_REAL_MODE ;print that we are starting
call print_string ; booting from 16-bit real mode
call load_kernel ; Load our kernel
call switch_to_pm ; Switch to protected mode, from which
; we will not return
jmp $
; Include our useful , hard-earned routines
%include "print/print_string.asm"
%include "disk/disk_load.asm"
%include "pm/gdt.asm"
%include "pm/print_string_pm.asm"
%include "pm/switch_to_pm.asm"
[bits 16]
; load_kernel
load_kernel:
mov bx, MSG_LOAD_KERNEL ; Print a message to say we are loading the kernel
call print_string
mov bx, KERNEL_OFFSET ; Set-up parameters for our disk_load routine , so
mov dh, 15 ; that we load the first 15 sectors (excluding
mov dl, [BOOT_DRIVE] ; the boot sector) from the boot disk (i.e. our
call disk_load ; kernel code) to address KERNEL_OFFSET
ret
[bits 32]
BEGIN_PM:
mov ebx, MSG_PROT_MODE ; Use 32-bit print routine to
call print_string_pm ; announce its in protected mode
;the program manages to come here perfectly.
;here the kmain() function is called via kernel_entry.asm
;but after this call, the program gets "lost", it ends somehow.
call KERNEL_OFFSET
;it cant even get here, it ends before getting here. Probably the kmain() is not located at KERNEL_OFFSET due to a problem.
jmp $ ;Hang.
; Global variables
BOOT_DRIVE db 0
MSG_REAL_MODE db "Started in 16-bit Real Mode", 0
MSG_PROT_MODE db "Successfully landed in 32-bit Protected Mode", 0
MSG_LOAD_KERNEL db "Loading kernel into memory.", 0
; Bootsector padding
times 510-($-$$) db 0
dw 0xaa55
kernel.c (c code)
void kmain()
{
char* screen = (char*)0xb8000;
screen[0] = 'X';
}
kernel_entry.asm (calls the c code)
[bits 32]
[extern _kmain]
call _kmain
jmp $
Finally, to obtain the os-image , I used the following commands
kernel.c and kernel_entry.asm are converted to object files (nasm and gcc commands), then they are linked to create kernel.bin file (by using ld command and the address 0x100000 of the kmain()).
Then kernel.bin and boot_sect.bin are combined using type command to obtain an os image.
gcc -m32 -ffreestanding -c kernel.c -o kernel.o
nasm kernel_entry.asm -f elf -o kernel_entry.o
ld -m i386pe -T NUL -o kernel.bin -Ttext-segment 0x100000 kernel_entry.o kernel.o
nasm boot_sect.asm -f bin -o boot_sect.bin
type boot_sect.bin kernel.bin > os-image

Related

how to access glibc data types from an assembly program using got? [duplicate]

This question already has answers here:
Access .data section in Position Independent Code
(1 answer)
What are the calling conventions for UNIX & Linux system calls (and user-space functions) on i386 and x86-64
(4 answers)
Closed 2 years ago.
I'm trying to access the stdin C FILE* type so i can call fgets to get input and echo it back. I can use plt section to access C functions but can't use .got section (which if i understand right is like plt but for data) in the same way for for stdin as a segmentation fault always result from mov instruction after lea
section .bss
BUFFERLEN equ 100
BUFFER: resb BUFFERLEN
section .text
global main
extern stdin, fgets
main:
default rel
push rbp
mov rbp, rsp
; First get input to print
mov rdi, [rel BUFFER]
mov rsi, BUFFERLEN
mov rdx, [rel stdin wrt ..gotpc]
call fgets wrt ..plt
pop rbp
ret ; return back to caller
built and linked with makefile as
proj13: proj13.o
gcc -pie -fpie -o proj13 proj13.o
proj13.o: proj13.asm
nasm -g -F DWARF -f elf64 -o proj13.o proj13.asm

How to fix: "Disk Read Error" in my bootloader

I am using Windows 10 and Windows subsystem for linux.
I have started creating my own Operating System in Assembly and C.
I am following a tutorial.
I got stuck into 2 problems.
Error 1:
When I Link and create bin files, i am getting a warning:
"ld: warning: cannot find entry symbol _start; defaulting to 0000000000001000"
Does this matter?
Error 2:
After compiling my code, there was no error. But when i boot my operating system, it shows an error: Disk read error!
Please help me.
Boot.asm
[org 0x7c00]
KERNEL_OFFSET equ 0x1000
mov [BOOT_DRIVE], dl
mov bp, 0x9000
mov sp, bp
mov si, MSG_REAL_MODE
call print
call load_kernel
call switch_to_pm
jmp $
%include "printstr.asm"
%include "diskload.asm"
[bits 16]
load_kernel :
mov si, MSG_LOAD_KERNEL
call print
mov bx, KERNEL_OFFSET
mov dh, 15
mov dl, [ BOOT_DRIVE ]
call disk_load
ret
[bits 32]
BEGIN_PM :
mov ebx, MSG_PROT_MODE
call print_string_pm
call KERNEL_OFFSET
jmp $
BOOT_DRIVE db 0
MSG_REAL_MODE db " Started in 16 - bit Real Mode " , 0
MSG_PROT_MODE db " Successfully landed in 32 - bit Protected Mode " , 0
MSG_LOAD_KERNEL db " Loading kernel into memory. " , 0
times 510 -( $ - $$ ) db 0
dw 0xaa55
diskload.asm
disk_load :
push dx
mov ah , 0x02
mov al , dh ;
mov ch , 0x00
mov dh , 0x00
mov cl , 0x02
int 0x13
jc disk_error
pop dx
cmp dh , al
jne disk_error
ret
disk_error :
mov si , DISK_ERROR_MSG
call prints
jmp $
; Variables
DISK_ERROR_MSG db " Disk read error !" , 0
prints:
lodsb
or al, al
jz printdones
mov ah, 0eh
int 10h
jmp prints
printdones:
ret
Compiling commands:
nasm boot.asm -f bin -o boot.bin
nasm kernel_entry.asm -f elf64 -o kernel_entry.o
gcc -ffreestanding -c kernel.c -o kernel.o
ld -o kernel.bin -Ttext 0x1000 kernel_entry.o kernel.o --oformat binary
cat boot.bin kernel.bin > os.iso
ld: warning: cannot find entry symbol _start; defaulting to 0000000000001000
The effect of this error will be that the entry point address stored in the executable file will not be correct.
However, "raw" binary files only containing some memory content; they don't contain any additional information - such as the entry point - as an ELF or COFF file would do.
In other words: For "raw" binary files (--oformat binary) this warning message has no meaning at all.
But when i boot my operating system, it shows an error: Disk read error!
I'm not sure, but there are two possible errors:
Are you sure that the ES register contains the correct value?
(If you want to load your kernel into absolute address 0x1000, you have to set ES to 0.)
Many drive types don't support reading too many sectors at once.
(However a real 1440K drive should support reading 15 sectors starting at sector #2.)
I had this problem too , the disk read error may be caused by your final os image being too small for 15 sectors read.
if you use qemu , try to resize your os image with :
qemu-img resize os.iso +20K
this is going to resize your image to 20 KB
(you can put your own value instead of 20K).
I think qemu considers the os image as the whole disk , so you should resize it accordingly .

Loading elf-i386 from my boot loader

I am doing operating system project, until now I have my bootloader running. I can load binary file using bios interuppt, but I am unable to load and call C function from ELF file format:
Here is my C program that I want to finally execute:
//build :: cc -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -c -o kmain.o kmain.c
void kmain(){
int a = 5;
for(;;);
}
Here is assembly code to call kmain()
; build :: nasm -f elf loader.asm
[BITS 32]
[GLOBAL start]
[EXTERN kmain]
section .text
start:
mov eax, 0
call kmain
This is my linker script
ENTRY(start)
and this how I am linking everything together
ld -m elf_i386 -T link.ld -o kernel loader.o kmain.o
Now to call start from my bootloader, I am using e_entry offset field from elf header( 24 byte away from starting address) :
xor edx, edx
mov edx, 24
add edx, IMAGE_PMODE_BASE
add ebx, dword[edx]
add ebx, IMAGE_PMODE_BASE
call ebx
where IMAGE_PMODE_BASE is address of elf file loaded in memory.
My question is "Is This the correct way of loading and calling a function in C in ELF file format?".
Thank you for reading, please help.

NASM assembled binary works, but shellcode in C causes segmentation fault

I am trying to learn about assembly and shellcode. I am having a problem that has been driving me nuts for days. I have written some assembly to make a system call to execve. The assembled file works, but when I convert it to shellcode and try to run it from a C program, I get a segmentation fault. I have tried all sorts of variations, but nothing works. Currently, the nasm code is as follows:
global _start
section .data
cmd: db '/bin/netcat', 0
argv0: db 'netcat', 0
argv1: db '127.0.0.1', 0
argv2: db '3333', 0
argv: dd argv0, argv1, argv2, 0
section .text
_start:
mov ebx, cmd ; filename parameter
mov ecx, argv ; argv parameter
xor edx, edx ; envp parameter
mov al, 11 ; execve system call number. Moving to al rather than eax to prevent null bytes in shellcode.
int 0x80
; exit
xor ebx, ebx ; status code 0
mov al, 1 ; sys_exit
int 0x80
I assembled this with the following command:
nasm -f elf32 test.asm -o test.o && ld -m elf_i386 test.o -o test
Running test works fine. I obtained the shellcode from test using the command from this page and of course, replacing ./PROGRAM with ./test.
Finally, I have the following C code:
unsigned char[] = "\xbb\x98\x90\x04\x08\xb9\xba\x90\x04\x08\x31\xd2\xb0\x0b\xcd\x80\xb0\x01\x31\xdb\xcd\x80";
main()
{
int (*ret)() = (int(*)())code;
ret();
}
Compiled with:
gcc -g -m32 -fno-stack-protector -z execstack testshellcode.c -o testshellcode
Running testshellcode results in a Segmentation fault. I have tried various things like making code a const, compiling with different flags, and about 1,000 variations of the nasm code. I have successfully run other shellcode that calls execve, but argv was simple in those cases.
Using gdb, I can see it crashes when ret() is called. Looking at the registers after the segmentation fault:
eax 0xffffffda -38
ecx 0x80490ba 134516922
edx 0x0 0
ebx 0x0 0
esp 0xbffff14c 0xbffff14c
ebp 0xbffff178 0xbffff178
eip 0x804a03a 0x804a03a <code+22>
eflags 0x10246 [ PF ZF IF RF ]
Any help would be highly appreciated.

JMP not working

Okay, so I've been trying to make a two-step bootloader in assembly/C but I haven't been able to get the JMP working. At first I thought the read was failing, but, after the following test I ruled that out:
__asm__ __volatile__(
"xorw %ax, %ax;"
"movw %ax, %ds;"
"movw %ax, %es;"
"movb $0x02, %ah;"
"movb $0x01, %al;"
"movw $0x7E00, %bx;"
"movw $0x0003, %cx;"
"xorb %dh, %dh;"
"int $0x13;"
"movb 0x7E00, %al;"
"movb $0x0e, %ah;"
"int $0x10;"
//"jmp 0x7E00"
);
This printed 'f' as expected (the first byte of the sector is 0x66 which is the ASCII code for 'f') proving that the read is successful and the jmp is the problem. This is my code:
__asm__(".code16\n");
__asm__(".code16gcc\n");
__asm__("jmpl $0x0000, $main\n");
void main(){
__asm__ __volatile__(
"xorw %ax, %ax;"
"movw %ax, %ds;"
"movw %ax, %es;"
"movb $0x02, %ah;"
"movb $0x01, %al;"
"movw $0x7E00, %bx;"
"movw $0x0003, %cx;"
"xorb %dh, %dh;"
"int $0x13;"
"jmp $0x200;"
);
}
When run, my program simply hangs, this means that the program is probably jumping to the wrong location in memory. By the way, I am obviously running this in real mode under VMWare player. I am compiling this with the following commands:
gcc -c -0s -march=i686 -ffreestanding -Wall -Werror boot.c -o boot.o
ld -static -Ttest.ld -nostdlib --nmagic -o boot.elf boot.o --no-check-sections
objcopy -0 binary boot.elf boot.bin
and this is test.ld:
ENTRY(main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(.test);
}
.sig : AT(0x7DFE)
{
SHORT(0xAA55);
}
}
Note: I have confirmed this is not a problem with the inline asm - I have tried a pure assembly implementation too with the same results - the only reason I am using C is because I plan on expanding this a bit and I am much more comfortable with C loops and functions...
EDIT: I've uploaded the first three sectors of my floppy drive here
EDIT 2: I have been unable to get my boot loader working using any of the suggestions and, based on advice from #RossRidge I have written an assembly version of the same program and a simple assembly program to echo input. Sadly these aren't working either..
Bootloader:
org 0x7c00
xor ax, ax
mov ds, ax
mov es, ax
mov ah, 0x02
mov al, 0x01
mov bx, 0x7E00
mov cx, 0x0003
xor dh, dh
int 0x13
jmp 0x7E00
Program in sector 3:
xor ax, ax
int 0x16
mov ah, 0xe
int 0x10
These are both compiled with: nasm Linux/boot.S -o Linux/asm.bin and behave the same as their C counterparts..
I am pretty sure your assembler is generating the wrong jump offset, it's probably interpreting the 0x7e00 as relative in the current text section, but that's mapped at 0x7c00 by the linker script so your jump probably goes to 0x7c00+0x7e00=0xfa00 instead. As an ugly workaround you could try jmp 0x200 instead or use an indirect jump to hide it from your tools such as mov $0x7e00, %ax; jmp *%ax. Alternatively jmp .text+0x200 could also work.
Also note that writing 16 bit asm in a 32 bit C compiler will generate wrong code, which is also hinted by the first byte of the loaded sector being 0x66 which is the operand size override prefix. Since the compiler assumes 32 bit mode, it will generate your 16 bit code with prefixes, which when run in 16 bit mode will switch back to 32 bits. In general, it's not a good idea to abuse inline asm for such purpose, you should use a separate asm file and make sure you tell your assembler that the code is intended for 16 bit real mode.
PS: learn to use a debugger.
Update: The following code when assembled using nasm boot.asm -o boot.bin works fine in qemu and bochs:
org 0x7c00
xor ax, ax
mov ds, ax
mov es, ax
mov ah, 0x02
mov al, 0x01
mov bx, 0x7E00
mov cx, 0x0003
xor dh, dh
int 0x13
jmp 0x7E00
times 510-($-$$) db 0
dw 0xaa55
; second sector
times 512 db 0
; Program in sector 3:
xor ax, ax
int 0x16
mov ah, 0xe
int 0x10
jmp $
times 1536-($-$$) db 0
You can download an image here (limited offer ;)).
I have written many boot loaders.
There needs to be two(2) separate executables,
1) the boot loader
2) the main code
so the loaded code (the main program) can be updated without doing anything to the boot loader.
The boot loader, when done needs to jump to a known location.
The main program needs to place a jump instruction at the known location, that jumps to a function in the librt library. (usually start())
start() will set I/O, heap, etc and call main()
Both the linker command file for the boot loader and the linker command file for the main program must agreed on the address of the known location
The known location is usually the only thing in a unique section of both linker command files.
The boot loader must be smart enough to place all the parts of the main program in the right areas in memory.
It is best if the main program is in motorola S1 (or similar) format rather than a raw .elf or .coff format; otherwise the boot loader can get very big very quickly.

Resources