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
Testing the kernel using grub modules
Testing the kernel using grub modules
“Meaningless! Meaningless!”
says the Teacher.
“Utterly meaningless!
Everything is meaningless.” - Ecclesiastes 1, 2
Educational Purpose Operating System - EPOS
says the Teacher.
“Utterly meaningless!
Everything is meaningless.” - Ecclesiastes 1, 2
Educational Purpose Operating System - EPOS
Re: Testing the kernel using grub modules
I wrote this code for my kernel:
If you need anything else just take a look at the source code of elfdump
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;
}
- BrightLight
- Member
- Posts: 901
- Joined: Sat Dec 27, 2014 9:11 am
- Location: Maadi, Cairo, Egypt
- Contact:
Re: Testing the kernel using grub modules
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:
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.
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
You know your OS is advanced when you stop using the Intel programming guide as a reference.
Re: Testing the kernel using grub modules
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
- Alan Kay
Re: Testing the kernel using grub modules
That's exactly what I don't want. I like to seperate tests from code itself.Roman wrote:Why not just use a C preprocessor macro for optionally including the tests?
too many #ifdefs makes me nuts.
“Meaningless! Meaningless!”
says the Teacher.
“Utterly meaningless!
Everything is meaningless.” - Ecclesiastes 1, 2
Educational Purpose Operating System - EPOS
says the Teacher.
“Utterly meaningless!
Everything is meaningless.” - Ecclesiastes 1, 2
Educational Purpose Operating System - EPOS