Testing the kernel using grub modules

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
stdcall
Member
Member
Posts: 78
Joined: Thu Mar 14, 2013 1:30 am

Testing the kernel using grub modules

Post by stdcall »

I had an idea for a way to unit test my kernel without putting unit tests code inline with the code it self.
I thought of the kernel executing a testing module passed by grub.

My only concern, is how to call the kernel functions from within module. I have to figure out the exact location of each function I want to test.
Like dlsym, or something equivalent.

Here's my question.
1. Is it a good way ? is there alternative way ?
2. Is there a way in the linkage of the grub module to provide it with the kernel functions addresses (kind of linking it with the kernel)
3. If not, is it possible to find the functions location using one of the kernel ELF section ? (runtime lookup)

Thanks.
Ramon
“Meaningless! Meaningless!”
says the Teacher.
“Utterly meaningless!
Everything is meaningless.” - Ecclesiastes 1, 2

Educational Purpose Operating System - EPOS
User avatar
Ch4ozz
Member
Member
Posts: 170
Joined: Mon Jul 18, 2016 2:46 pm
Libera.chat IRC: esi

Re: Testing the kernel using grub modules

Post by Ch4ozz »

I wrote this code for my kernel:

Code: Select all

uint32_t elf_get_proc_address(Elf32_Ehdr *header, void *image, char *funcname)
{
	Elf32_Shdr *section = (Elf32_Shdr*)(((uint32_t)header) + header->e_shoff);
	Elf32_Phdr *section_ph = (Elf32_Phdr*)(((uint32_t)header) + header->e_phoff);
	
	uint32_t base_address = 0xFFFFFFFF;
	
	int i;
	for(i = 0; i < header->e_phnum; i++)
	{
		if(section_ph[i].p_type == 1)
		{
			base_address = section_ph[i].p_vaddr;
			break;
		}
	}
	
	Elf32_Shdr *sym_name_table = 0;
	Elf32_Shdr *sectab = (Elf32_Shdr*)((uint32_t)header + header->e_shoff);
	uint32_t sectab_size = header->e_shnum * header->e_shentsize;
	
	for(i = 0; i < header->e_shnum; i++)
	{
		if(section[i].sh_name)
		{
			char *section_name = (char*)((uint32_t)header + section[header->e_shstrndx].sh_offset + section[i].sh_name);
			
			if(!strcmp(section_name, ".strtab"))
			{
				sym_name_table = (Elf32_Shdr*)((uint32_t)header + section[i].sh_offset);
				break;
			}
		}
	}
	
	if(!sym_name_table)
		return 0;
	
	Elf32_Shdr *ptr = (Elf32_Shdr*)sectab;
	while (ptr->sh_type != SHT_SYMTAB)
		ptr++;	
	
	int num_syms = ptr->sh_size / ptr->sh_entsize;
	Elf32_Shdr *sym_tab = (Elf32_Shdr*)((uint32_t)header + ptr->sh_offset);
	Elf32_Sym *symbol = (Elf32_Sym*)sym_tab;
	
	for (i = 0; i < num_syms; i++)
	{
		if (ELF32_ST_BIND(symbol->st_info) == STB_GLOBAL)
		{
			if(ELF32_ST_TYPE(symbol->st_info) == STT_FUNC)
			{
				if(!strcmp((char*)((uint32_t)sym_name_table + symbol->st_name), funcname))
					return symbol->st_value - base_address + (uint32_t)image;
			}
			
			/*if (ELF32_ST_TYPE(symbol->st_info) == STT_FUNC)
			{
				printf("\n Function:     %s\n", ((uint32_t)sym_name_table + symbol->st_name));
				printf(" Address:      0x%X\n", symbol->st_value);
			}
			else if (ELF32_ST_TYPE(symbol->st_info) == STT_OBJECT)
			{
				printf("\n Data Object:  %s\n", ((uint32_t)sym_name_table + symbol->st_name));
				printf(" Size:  %d\t\tValue:  0x%X (%d)\n", symbol->st_size, symbol->st_value, symbol->st_value);
			}*/
		}
		
		symbol++;
	}
	
	return 0;
}
If you need anything else just take a look at the source code of elfdump :)
User avatar
BrightLight
Member
Member
Posts: 901
Joined: Sat Dec 27, 2014 9:11 am
Location: Maadi, Cairo, Egypt
Contact:

Re: Testing the kernel using grub modules

Post by BrightLight »

I've tried to understand your question the best I could, and the solution is system calls.
I am assuming your GRUB module will be a test program to run and use the kernel's services for printing to screen/getting input/etc... The kernel provides this interface using system calls. There are several ways to implement system calls, the most common being interrupts (e.g. INT 0x21 in DOS and INT 0x80 in Linux), or SYSCALL instruction for x86_64 platform.
You don't need to know the exact address of each function. You assign each function a code number, starting from zero and numbered up to as many functions your kernel provides. For example, my system call handler looks like:

Code: Select all

; kernel_api:
; INT 0x60 Handler
; In\	EBP = Function code
; In\	All other registers = Depends on function input
; Out\	All registers = Depends on function output; all undefined registers destroyed
align 32
kernel_api:
	cmp ebp, MAXIMUM_FUNCTION
	jg .done

	sti

	shl ebp, 2	; mul 4
	add ebp, api_table
	mov ebp, [ebp]
	call ebp

.done:
	iret
Where api_table is a list of pointers to all system calls, at build time. No need to know the exact address of each function. ;)
You know your OS is advanced when you stop using the Intel programming guide as a reference.
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: Testing the kernel using grub modules

Post by Roman »

Why not just use a C preprocessor macro for optionally including the tests?
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
stdcall
Member
Member
Posts: 78
Joined: Thu Mar 14, 2013 1:30 am

Re: Testing the kernel using grub modules

Post by stdcall »

Roman wrote:Why not just use a C preprocessor macro for optionally including the tests?
That's exactly what I don't want. I like to seperate tests from code itself.
too many #ifdefs makes me nuts.
“Meaningless! Meaningless!”
says the Teacher.
“Utterly meaningless!
Everything is meaningless.” - Ecclesiastes 1, 2

Educational Purpose Operating System - EPOS
Post Reply