Page 1 of 1

[Solved]Apparent Corruption From Changing Kernel Link Order

Posted: Fri Sep 25, 2009 4:30 pm
by skwee
Strange Behaviour with different linking order

Hi there!
I rewrote my makefile to handle most of the work automatically (before this I needed to add each file by hand).
Any way, I noticed that the linking order affects the behavior of the console "driver".

Here are the relevant code:
kmain.h

Code: Select all

#ifndef _KERNEL32_H_
#define _KERNEL32_H_

#include <boot/multiboot.h>

/*
 * Kernel entry point.
 */
extern int kmain(multiboot_info_t *mbi);

#endif
kmain.c

Code: Select all

#include <kmain.h>
#include <io/console.h>
#include <arch/i386/cpu.h>
#include <arch/i386/gdt.h>
#include <arch/i386/idt.h>
#include <arch/i386/pic.h>
#include <arch/i386/pit.h>
#include <arch/i386/intr.h>
#include <arch/i386/paging.h>
#include <mm/pmm.h>

/* Initialize Kernel. */
static void kinitialize(multiboot_info_t *mbi);

/* Parse multiboot info */
static void kparse_multiboot_info(multiboot_info_t *mbi);

/* PIT handler function */
static void kpit_handler(intr_registers_t regs);

/* Kernel entry point */
extern int kmain(multiboot_info_t *mbi){
	//Init console
	console_init();

	//Initialize
	kinitialize(mbi);
	
	//Enable interrupts
	intr_enable();

	for(;;){}

	return 0;
}


/* Kernel initialize */
static void kinitialize(multiboot_info_t *mbi){
	//100Hz frequency, every 10ms IRQ0 fired
	const uint32_t pit_frequency = 100;
	
	console_set_color(MAKE_COLOR(0, CL_BLACK, 1, CL_WHITE));
	
	kprintf("Booting kernel....\n");
	
	kprintf("Setting up GDT... ");
	gdt_init();
	kprintf("Done!\n");
	
	kprintf("Setting up IDT and interrupts... ");
	idt_init();
	kprintf("Done!\n");
	
	kprintf("Remapping PIC... ");
	pic_init();
	kprintf("Done!\n\tMaster PIC vector offset 0x%.2X\n\tSlave  PIC vector offest 0x%.2X\n",
		PIC_MASTER_ISR, PIC_SLAVE_ISR);
		
	kprintf("Setting PIT channel0 to %dHz...", pit_frequency);
	pit_init(pit_frequency, kpit_handler);
	kprintf("Done!\n");
	
	kprintf("Setting up Physical Memory Manager... ");
	pmm_init((mbi->mem_lower + mbi->mem_upper) * 1024);
	kprintf("Done!\n");
	
	kparse_multiboot_info(mbi);
	
	kprintf("Initializing paging... ");
	pg_init();
	kprintf("Done!\n");
	
	{
		uint32_t total_mem, used_mem, reserved_mem, free_mem;
		pmm_get_report(&total_mem, &used_mem, &reserved_mem, &free_mem);
		kprintf("\n::pmm report::\n");
		kprintf("\tTotal Memory: %dKiB\n\tFree memory: %dKiB\n\tUsed Memeory: %dKiB\n\tReserved Memory: %dKiB\n",
			total_mem / 1024, free_mem / 1024, used_mem / 1024,
				reserved_mem / 1024);
	}
}

/* Multiboot parse */
static void kparse_multiboot_info(multiboot_info_t *mbi){
	//Parses the mutiboot and prints memory map
}


/* PIT handler function */
static void kpit_handler(intr_registers_t __attribute__((unused))regs){
}
console.c - The actual file the is responsible for writing to 0xB8000

Code: Select all

#include <io/console.h>
#include <arch/i386/io.h>

static cursor_loc_t	_cursor_x, _cursor_y; //Cursor position
static uint16_t		*const _video_memory = (uint16_t *)VIDEO_MEMORY_START;
static color_t		_attributes; //Current attributes


/* Check if given char is escape char (aka \n, \t...) */
static uint8_t is_escape_char(char ch);

/* Parse escape character. */
static void parse_escape_char(char ch);

/* Helper *printf function. */
static int doprintf(const char *fmt, va_list args);

/* Update cursor position. */
static void update_cursor(void);

/* Make sure cursor is not out of bounds */
static void assert_cursor_bounds(void);

/* Scroll screen */
static void scroll_screen(void);

/* Put character to screen. */
static void put_char(char ch);

/* Get number of digits in given number. */
static uint32_t get_number_of_digits(unsigned int num, uint8_t base,
		int *multiplier);

/* Put number. */
static void put_num(int num, int width, int precision, uint8_t base, 
		bool_t hex_in_upper);


/* Init console. */
void console_init(void){
	//Set initial cursor position, and attributes
	_cursor_x = _cursor_y = 0;
	_attributes = MAKE_COLOR(0, CL_BLACK, 0, CL_WHITE);

	//Clear the screen
	console_clear();
}

/* Clear screen */
void console_clear(void){
	color_t color = MAKE_COLOR(0, CL_BLACK, 0, CL_WHITE);
	uint16_t i;
	uint16_t blank = (uint16_t)((color << 8) | 0x20); //Space character

	//Fill screen with blank characters
	for(i = 0; i < CONSOLE_SCREEN_H * CONSOLE_SCREEN_W; ++i){
		_video_memory[i] = blank;
	}

	//Update cursor position.
	_cursor_y = _cursor_x = 0;

	//Update cursor hardware.
	update_cursor();
}

/* Set color */
void console_set_color(color_t cl){
	_attributes = cl;
}

/* Set cursor location */
void console_gotoxy(cursor_loc_t x, cursor_loc_t y){
	if(x < CONSOLE_SCREEN_W)	_cursor_x = x;
	if(y < CONSOLE_SCREEN_H)	_cursor_y = y;
}

/* Kernel printf. */
int kprintf(const char *str, ...){
	int ret;
	va_list args;

	//Prepare the arguments and call helping function
	va_start(args, str);
	ret = doprintf(str, args);
	va_end(args);

	//Update cursor
	update_cursor();

	return ret;
}


/* is escape char. */
static bool_t is_escape_char(char ch){
	if((ch == '\n') || (ch == '\t'))
		return TRUE;
	return FALSE;
}

/* parse escape char */
static void parse_escape_char(char ch){
	if(ch == '\n'){ //New line
		_cursor_x = 0;
		_cursor_y ++;
	}else if(ch == '\t'){ //Tab char
		_cursor_x = (uint8_t)(_cursor_x + CONSOLE_TAB_SIZE);
	}

	//Make sure cursor is not out of bounds.
	assert_cursor_bounds();
}

/* do printf */
static int doprintf(const char *fmt, va_list args){
	int count = 0; //How much we've printed?
	bool_t is_specifier = FALSE; //Are we in special situation?
	bool_t is_precision = FALSE; //Are we setting the precision?
	bool_t some_printed = FALSE; //Have we just printed to screen?
	int width, precision; //Width and precision.
	width = precision = 0;
	

	//Keep iteration on the string while we can
	while(*fmt){
		
		if(is_specifier){ //If we encountered a % in prev character
			if(*fmt == 'c'){ //Handle single char print
				char ch = (char)va_arg(args, int);
				put_char(ch);
				count ++;
				
				some_printed = TRUE;
			}else if(*fmt == 's'){ //Handle string print
				char *ch = (char *)va_arg(args, int);
				count += kprintf(ch);
				
				some_printed = TRUE;
			}else if((*fmt == 'd') || (*fmt == 'i')){ //decimal number
				int num = va_arg(args, int);
				put_num(num, width, precision, 10, FALSE);
				count ++;
				
				some_printed = TRUE;
			}else if(*fmt == 'x'){ //Hexadecimal number
				int num = va_arg(args, int);
				put_num(num, width, precision, 16, FALSE);
				count ++;
				
				some_printed = TRUE;
			}else if(*fmt == 'X'){ //Uppercase hexadecimal
				int num = va_arg(args, int);
				put_num(num, width, precision, 16, TRUE);
				count ++;
				
				some_printed = TRUE;
			}else if((*fmt >= '0') && (*fmt <= '9')){ //Width
				//Calculate with or precision
				if(is_precision) precision = *fmt - '0';
				else width = *fmt - '0';
				
				is_specifier = FALSE; //Small hack to stay in specifier
									  //mode :)
				is_precision = FALSE;
			}else if(*fmt == '.'){ //Precision mode
				is_precision = TRUE; //Set percision and not width
				is_specifier = FALSE; //Small hack to stay in specifier
									  //mode :)
			}else if(*fmt == '%'){ //Double % char prints one % char
				put_char('%');
				count ++;
			}
			
			//If we printed number, erease
			if(some_printed){
				some_printed = FALSE;
				width = precision = 0;
			}

			//Inverse specifier
			is_specifier = ! is_specifier;
		}else{
			if(is_escape_char(*fmt)){ //Check for escape character
				parse_escape_char(*fmt);
			}else if(*fmt == '%'){ //Check for specifier mode
				is_specifier = 1; //Specifier mode
			}else{ //Any other case we just print the charater it self
				put_char(*fmt);
				count ++;
			}
		}

		//Move to next char
		fmt ++;
	}

	return count;
}

/* Update cursor */
static void update_cursor(void){
	//Calculate cursor location
	uint16_t cur_loc = (uint16_t)(_cursor_y * CONSOLE_SCREEN_W + _cursor_x);

	//Say VGA controller that we send the high cursor location
	outb(CRT_DR_PORT, CMD_CUR_HI_LOC);
	//And send hight cursor location
	outb(CRT_IR_PORT, (uint8_t)((cur_loc >> 8) & 0xFF));

	//Say VGA controller that we send the low cursor location
	outb(CRT_DR_PORT, CMD_CUR_LO_LOC);
	//And send low cursor location
	outb(CRT_IR_PORT, (uint8_t)(cur_loc & 0xFF));
}

/* Assert cursor bounds. */
static void assert_cursor_bounds(void){
	//Check X bound
	if(_cursor_x >= CONSOLE_SCREEN_W){
		_cursor_x = 0;
		_cursor_y ++;
	}

	//Check Y bound
	if(_cursor_y >= CONSOLE_SCREEN_H){
		scroll_screen();
	}
}

/* Scroll screen. */
static void scroll_screen(void){
	uint16_t i;
	color_t attr = MAKE_COLOR(0, CL_BLACK, 0, CL_WHITE);
	uint16_t blank = (uint16_t)((attr << 8) | 0x20); //Space char

	//Each line goes up
	for(i = 0; i < (CONSOLE_SCREEN_H - 1) * CONSOLE_SCREEN_W; ++i){
		_video_memory[i] = _video_memory[i + CONSOLE_SCREEN_W];
	}

	//Last line clear
	for(i = (CONSOLE_SCREEN_H - 1) * CONSOLE_SCREEN_W;
			i < CONSOLE_SCREEN_H * CONSOLE_SCREEN_W; ++i){
		_video_memory[i] = blank;
	}

	//Update cursor position
	_cursor_y = CONSOLE_SCREEN_H - 1;
	_cursor_x = 0;
}

/* put char */
static void put_char(char ch){
	_video_memory[_cursor_y * CONSOLE_SCREEN_W + _cursor_x] =
		(uint16_t)((_attributes << 8) | ch);

	_cursor_x ++;

	assert_cursor_bounds();
}

/* Get number of digits. */
static uint32_t get_number_of_digits(unsigned int num, uint8_t base,
		int *multiplier){
	int mult = 1; //Multiplier
	uint32_t digit_count = 1; //Digit count
	
	//Start dividing the number by base untill we reach 0
	while((num / base) != 0){
		num = num / base;
		mult *= base;
		digit_count ++;
	}
	
	//If user requested the mutiplier, provide it to him
	if(multiplier != NULL) *multiplier = mult;
	
	//Return digit count.
	return digit_count;
}

/* Print number. */
static void put_num(int num, int width, int precision, uint8_t base, 
		bool_t hex_in_upper){
	//Array of possible digits
	static const char digits[] = {  '0', '1', '2', '3', '4', '5', '6',
									'7', '8', '9', 'a', 'b', 'c', 'd',
									'e', 'f' };
	int multiplier; //Multiplier
	uint32_t digit_count = 0; //Number of digits in number
	bool_t is_negative = FALSE; //Is the number negative?
	unsigned int num_to_print;
	
	//No support for higher base than hexadecimal yet
	//@TODO add support!
	if(base > 16) return;
	
	//If number negative mark it as negative and decrease width
	if(num < 0){
		is_negative = TRUE;
		num_to_print = (unsigned int)(-num); //Inverse the number
		
		//For 10 base we have to decrease the width and precision
		//because of minus sign before the number
		if(base == 10){ 
			width --;
			precision --;
		}else{ //For any other base we have to use ones complement
			num_to_print = ~ num_to_print; //swap bits
			num_to_print += 1; //add one
		}
	}else{
		num_to_print = num; //In any other case its just a number
	}
	
	//Calculate number of digits
	digit_count = get_number_of_digits(num_to_print, base, &multiplier);
	
	//Left padding characters are X - digit_count
	width -= digit_count;
	precision -= digit_count;
	
	//Pad the left space with blank character
	while(width > 0){
		put_char(' ');
		width--;
	}
	
	//Only for 10 base we put a minus sign before!
	if(is_negative && (base == 10)){
		put_char('-');
	}
	
	//Fill left space with leading zeros
	while(precision > 0){
		put_char('0');
		precision --;
	}
	
	//Print the number
	while(multiplier != 0){
		//Get most left digit
		unsigned int digit = num_to_print / multiplier;
		
		if((digit >= 10) && (hex_in_upper)){
			put_char(digits[digit] - 32);
			//32 = Difference between A and a in ascii table
		}else{
			//Print the digit
			put_char(digits[digit]);
		}
		
		//Get rid of the most left digit
		num_to_print = num_to_print % multiplier;
		
		//Decrease multiplier
		multiplier /= base;
	}
}
And the makefile:

#------------------------------------------
# Directories
#------------------------------------------
OBJDIR = build/
KERNELSRC =
KERNELINC = include/

#------------------------------------------
# Tools
#------------------------------------------
CC = @gcc
AS = @nasm
LD = @ld
AR = @ar
PRINT = @printf

#------------------------------------------
# Flags
#------------------------------------------
CCFLAGS = -c -std=gnu99 -Wall -Wextra -Winline -Werror -nostdlib -nostdinc \
-fno-builtin -fno-stack-protector -nodefaultlibs \
-pedantic -Wshadow -Wcast-align -Wmissing-declarations \
-Wredundant-decls -Wnested-externs \
-Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings \
-I$(KERNELINC)
ASFLAGS = -f elf
LDFLAGS = -Tlinker.ld
ARFLAGS = -rcs

#------------------------------------------
# Files
#------------------------------------------
ENTRY_FILES := $(wildcard $(KERNELSRC)*.c $(KERNELSRC)*.asm)
ENTRY_OBJECTS := $(ENTRY_FILES)
ENTRY_OBJECTS := $(ENTRY_OBJECTS:.asm=.o)
ENTRY_OBJECTS := $(ENTRY_OBJECTS:.c=.o)

ADT_FILES := $(wildcard $(KERNELSRC)adt/*.c)
ADT_OBJECTS := $(ADT_FILES:.c=.o)

I386_FILES := $(wildcard $(KERNELSRC)arch/i386/*.c \
$(KERNELSRC)arch/i386/*.asm)
I386_OBJECTS := $(I386_FILES)
I386_OBJECTS := $(I386_OBJECTS:.c=.o)
I386_OBJECTS := $(I386_OBJECTS:.asm=.o)

IO_FILES := $(wildcard $(KERNELSRC)io/*.c)
IO_OBJECTS := $(IO_FILES:.c=.o)

LIB_FILES := $(wildcard $(KERNELSRC)lib/*.c)
LIB_OBJECTS := $(LIB_FILES:.c=.o)

MM_FILES := $(wildcard $(KERNELSRC)mm/*.c)
MM_OBJECTS := $(MM_FILES:.c=.o)


OBJECTS = \
$(patsubst %, $(OBJDIR)%, $(ENTRY_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(IO_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(I386_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(MM_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(LIB_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(ADT_OBJECTS))


BINARY_NAME = $(OBJDIR)kernel32.bin

#------------------------------------------
# Rules
#------------------------------------------
kernel: $(OBJECTS) kernel_link

kernel_link:
$(PRINT) "\033[0;34m[LD ]\033[0m $(BINARY_NAME) \n"
$(LD) $(LDFLAGS) $(OBJECTS) -o $(BINARY_NAME)

$(OBJDIR)%.o: %.c
@mkdir -p $(OBJDIR)$(dir $@)
$(PRINT) "\033[0;34m[CC ]\033[0m $< \n"
$(CC) $(CCFLAGS) $< -o $@

$(OBJDIR)%.o: %.asm
@mkdir -p $(OBJDIR)$(dir $@)
$(PRINT) "\033[0;34m[AS ]\033[0m $< \n"
$(AS) $(ASFLAGS) $< -o $@

image: $(BINARY_NAME)
$(PRINT) "\033[0;34mCreating Image.\033[0m"
@(cp $(BINARY_NAME) ../../obj/)
$(PRINT) "\033[0;34m.\033[0m"
@(cd ../../; sh ./install.sh)
$(PRINT) "\033[0;34m.Done!\033[0m\n"

clean:
rm -r $(OBJDIR)
I highlighted the relevant area, notice that IO_OBJECTS are placed directly after the kernel.

This is the normal behavior: (sorry for poor quality)
Image

Now if I change the position of IO_OBJECTS inside OBJECTS lets say like this:

Code: Select all

OBJECTS = \
	$(patsubst %, $(OBJDIR)%, $(ENTRY_OBJECTS)) \
	$(patsubst %, $(OBJDIR)%, $(I386_OBJECTS)) \
	$(patsubst %, $(OBJDIR)%, $(MM_OBJECTS)) \
	$(patsubst %, $(OBJDIR)%, $(LIB_OBJECTS)) \
	$(patsubst %, $(OBJDIR)%, $(IO_OBJECTS)) \
	$(patsubst %, $(OBJDIR)%, $(ADT_OBJECTS))
This is how it looks:
Image
You see that most of the test was gone.

I hope you could help me with this!
Thanks a lot!

Re: Apparent Corruption From Changing Kernel Link Order[Strange]

Posted: Sat Sep 26, 2009 8:41 am
by skwee
berkus Thank you for the hint!

I solved the issues. The problem was in memset function, apparently it sets few bytes more, this caused the corruption of the memory.
For some reason I used an int pointer instead of char pointer.