Page 1 of 1

Testing the kernel using grub modules

Posted: Sat Sep 10, 2016 1:05 pm
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

Re: Testing the kernel using grub modules

Posted: Sat Sep 10, 2016 1:32 pm
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 :)

Re: Testing the kernel using grub modules

Posted: Sat Sep 10, 2016 1:33 pm
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. ;)

Re: Testing the kernel using grub modules

Posted: Sat Sep 10, 2016 2:01 pm
by Roman
Why not just use a C preprocessor macro for optionally including the tests?

Re: Testing the kernel using grub modules

Posted: Sun Sep 11, 2016 11:46 am
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.