Calling some function makes kernel unbootable
Posted: Sat Oct 24, 2009 7:59 am
I hope somebody could help with very strange problem I've ecountered (it seems stange to me, but it's quite possible that it's something very normal). I'm writing simple, 32-bit OS for x86, for educational purposes (so its source is damn readable:) ). Here you got some files that could be useful:
linker.ld
loader.asm
include/kernel/terminal.h
kernel/terminal.c
and finally... main.c
I compile loader.asm with nasm -f elf and C files with gcc -Wall -Wextra -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -fno-leading-underscore.
The problem is, that using print_formatted() function in k_main[] makes kernel image not multiboot-compatible...
Without print_formatted() calling:
And with:
I see there's something wrong with .data section apperance and generally sections mixing, but I don't understand what all of this mean.
PS. Non-related comments and advices are also welcome.
linker.ld
Code: Select all
OUTPUT_FORMAT("elf32-i386")
ENTRY(loader)
phys = 0x00100000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
}
Code: Select all
[BITS 32]
global loader
mboot:
; Multiboot macros to make a few lines later more readable
MULTIBOOT_PAGE_ALIGN equ 1<<0
MULTIBOOT_MEMORY_INFO equ 1<<1
MULTIBOOT_AOUT_KLUDGE equ 1<<16
MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
; This part MUST be 4byte aligned
align 4
; Declared in linker.ld
extern code, bss, end
; This is the GRUB Multiboot header. A boot signature
dd MULTIBOOT_HEADER_MAGIC
dd MULTIBOOT_HEADER_FLAGS
dd MULTIBOOT_CHECKSUM
dd mboot
dd code
dd bss
dd end
dd loader
loader:
; This points the stack to our new stack area
mov esp, _system_stack
push eax ; Multiboot magic number
push ebx ; Multiboot info structure
extern k_main
call k_main
hlt
; Remember that a stack actually grows
; downwards, so we declare the size of the data before declaring
; the identifier '_system_stack'
SECTION .bss
resb 8192 ; This reserves 8KBytes of memory here
_system_stack:
Code: Select all
#ifndef _KERNEL_TERMINAL_H
#define _KERNEL_TERMINAL_H
extern const uint64_t VIDEO_RAM_ADDRESS;
extern const uint16_t TERMINAL_COLUMNS;
extern const uint16_t TERMINAL_LINES;
typedef struct terminal_cursor
{
uint16_t x;
uint16_t y;
} terminal_cursor_t;
extern void itoa (char *destination, const char base, const int32_t integer);
extern void putchar (const char ch);
extern void print_formatted (const char *format, ...);
extern void clear_screen ();
extern void init_terminal ();
#endif /* _KERNEL_TERMINAL_H */
Code: Select all
#include <kernel/types.h>
#include <kernel/terminal.h>
const uint64_t VIDEO_RAM_ADDRESS = 0xB8000;
const uint16_t TERMINAL_COLUMNS = 80;
const uint16_t TERMINAL_LINES = 25;
const uint8_t ATTRIBUTE = 0x07;
volatile uint8_t *video_ram;
static terminal_cursor_t cursor;
void
itoa (char *destination, const char base, const int32_t integer)
{
char *buffer = destination;
char *temp1, *temp2;
uint32_t unsigned_integer = integer;
uint16_t divisor = 10;
/* If %d is specified and integer is minus, put `-' in the head. */
if (base == 'd' && integer < 0) {
*buffer++ = '-';
destination++;
unsigned_integer = -integer;
} else if (base == 'x') {
divisor = 16;
}
/* Divide UD by DIVISOR until UD == 0. */
do {
int remainder = unsigned_integer % divisor;
*buffer++ = ((remainder < 10) ? remainder + '0' : remainder + 'a' - 10);
} while (unsigned_integer /= divisor);
/* Terminate BUF. */
*buffer = 0;
/* Reverse BUF. */
temp1 = buffer;
temp2 = buffer - 1;
while (temp1 < temp2) {
uint8_t temp = *temp1;
*temp1 = *temp2;
*temp2 = temp;
temp1++;
temp2--;
}
}
void
put_character (const char ch)
{
if (ch == '\n' || ch == '\r')
{
_NEWLINE:
cursor.x = 0;
cursor.y++;
if (cursor.y >= TERMINAL_LINES)
cursor.y = 0;
return;
}
*(video_ram + (cursor.x + cursor.y * TERMINAL_COLUMNS) * 2) = ch & 0xFF;
*(video_ram + (cursor.x + cursor.y * TERMINAL_COLUMNS) * 2 + 1) = ATTRIBUTE;
cursor.x++;
if (cursor.x >= TERMINAL_COLUMNS)
goto _NEWLINE;
}
/* not important things... */
void
print_formatted (const char *format, ...)
{
char **arguments = (char **) &format;
char ch;
char buffer[20];
arguments++;
while ((ch = *format++) != 0) {
if (ch != '%') {
put_character(ch);
} else {
char *next_ch;
ch = *format++;
switch (ch)
{
case 'd':
case 'u':
case 'x':
itoa(buffer, ch, *((int32_t *)arguments++));
next_ch = buffer;
goto _STRING;
break;
case 's':
next_ch = *arguments++;
if (!next_ch)
next_ch = "(null)";
_STRING:
while (*next_ch)
put_character(*next_ch++);
break;
default:
put_character(*((char *)arguments++));
break;
}
}
}
}
void
init_terminal ()
{
video_ram = (volatile uint8_t *)VIDEO_RAM_ADDRESS;
clear_screen();
print_formatted("Hello World!\n");
print_formatted("2 + 2 = %d!\n", 2 + 2);
print_formatted("...which is in hexadecimal... %x!\n", 2 + 2);
}
Code: Select all
#include <kernel/types.h>
#include <kernel/terminal.h>
void
k_main (uint64_t magic, uint64_t *multiboot_informations)
{
init_terminal();
put_character('a'); // Just fine
/* HERE'S THE PROBLEM */
print_formatted("Anything");
/* / HERE'S THE PROBLEM */
while (0==0)
/* nothing */;
}
The problem is, that using print_formatted() function in k_main[] makes kernel image not multiboot-compatible...
Without print_formatted() calling:
Code: Select all
mikoskay@mikoskay:~/Projects/os$ mbchk bin/kernel.bin
bin/kernel.bin: The Multiboot header is found at the offset 4736.
bin/kernel.bin: Page alignment is turned on.
bin/kernel.bin: Memory information is turned on.
bin/kernel.bin: Address fields is turned on.
bin/kernel.bin: All checks passed.
mikoskay@mikoskay:~/Projects/os$ objdump -h bin/kernel.bin
bin/kernel.bin: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00001000 00100000 00100000 00001000 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .rodata.str1.1 00000022 00101000 00101000 00002000 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .rodata.str1.4 00000024 00101024 00101024 00002024 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .data 00000fb8 00101048 00101048 00002048 2**3
CONTENTS, ALLOC, LOAD, DATA
4 .bss 00003004 00102000 00102000 00003000 2**3
ALLOC
5 .comment 0000001c 00000000 00000000 00003000 2**0
CONTENTS, READONLY
Code: Select all
mikoskay@mikoskay:~/Projects/os$ mbchk bin/kernel.bin
bin/kernel.bin: No Multiboot header.
mikoskay@mikoskay:~/Projects/os$ objdump -h bin/kernel.bin
bin/kernel.bin: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .rodata.str1.1 0000002b 00000000 00000000 00001000 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .rodata.str1.4 00000024 0000002c 0000002c 0000102c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .text 00001000 00100000 00100000 00002000 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
3 .bss 00003004 00101000 00101000 00003000 2**3
ALLOC
4 .comment 0000001c 00000000 00000000 00003000 2**0
CONTENTS, READONLY
PS. Non-related comments and advices are also welcome.