Page 1 of 1

Module loading

Posted: Thu Nov 03, 2022 7:27 am
by WinExperements
Hello! I have problem with the kernel module loading.
So after the module loaded, i got #UD and the problem is: the kernel symbols not resolving(address of functions didn't setted)
What i am doing wrong?
There are function for module symbols resolving:

Code: Select all

bool module_resolve_symbols(module_t *mod,Elf32_Ehdr *e) {
	unsigned i;
	Elf32_Shdr *s;
	Elf32_Sym *sym;
	const char *str;
	Elf32_Word size,entsize;
	for (i = 0, s = (Elf32_Shdr *)((char *)e + e->e_shoff); i < e->e_shnum;
		i++,s = (Elf32_Shdr *)((char *)s + e->e_shentsize)) {
		if (s->sh_type == SHT_SYMTAB) break;
	}
	if (i == e->e_shnum) {
		printf("%s: failed to find symbol table in module!\n");
		return false;
	}
	mod->symtab = (Elf32_Sym *)((char *)e + s->sh_offset);
	sym = mod->symtab;
	size = s->sh_size;
	entsize = s->sh_entsize;
	s = (Elf32_Shdr *)((char *)e + e->e_shoff + e->e_shentsize * s->sh_link);
	str = (char *)e+ s->sh_offset;
	for (i = 0; i < size/entsize; i++,sym = (Elf32_Sym *)((char *)sym + entsize)) {
		uint8_t type = ELF32_ST_TYPE(sym->st_info);
		uint8_t bind = ELF32_ST_BIND(sym->st_info);
		const char *name = str + sym->st_name;
		switch (type) {
			case STT_NOTYPE:
			case STT_OBJECT:
			if (sym->st_name != 0 && sym->st_shndx == 0) {
				sym->st_value = (Elf32_Addr)symbols_findValue(name);
				printf("Symbol %s founded in kernel: %x\n",name,sym->st_value);
				if (!sym->st_value) {
					printf("Cannot find symbol %s in kernel, abort\n",name);
					return false;
				}
			} else {
				sym->st_value += (Elf32_Addr) module_get_section_addr(mod,sym->st_shndx);
				if (bind != STB_LOCAL) {
					printf("TODO: add this to global symbol table: %s\n",name);
				}
			} break;
			case STT_FUNC:
			sym->st_value += (Elf32_Addr) module_get_section_addr(mod,sym->st_shndx);
			if (bind != STB_LOCAL) {
				printf("TODO: This function %s also need to be added to global symbols table\n",name);
			}
			if (strcmp(name,"module_main")) {
				printf("Module init found\n");
				mod->init = (void (*)(module_t *))sym->st_value;
			}
			break;
			case STT_SECTION:
			sym->st_value += (Elf32_Addr) module_get_section_addr(mod,sym->st_shndx);
			break;
			case STT_FILE:
			sym->st_value = 0;
			break;
			default:
			printf("Unknown symbol type: %d,%s\n",type,name);
			return false;
			break;
		}
	}
	return true;
}

Re: Module loading

Posted: Thu Nov 03, 2022 9:45 am
by nullplan
OK, yes, that does resolve the symbols. But do you at any point process the relocations? After assigning values to all the symbols, you must look through all the section headers again, looking for all sections of type SHT_REL and SHT_RELA. Each of those says in the section header's link field what symbol table they are referring to (the section index of that), and in the info field, what section they are relocations for. The offsets in the relocation structures are relative to the start of the referred-to section. And then you only have to actually process the relocations by type.

Re: Module loading

Posted: Thu Nov 03, 2022 10:32 am
by WinExperements
nullplan wrote:But do you at any point process the relocations?
Yeah i do it here:

Code: Select all

bool arch_relocSymbols(module_t *mod,void *ehdr) {
	Elf32_Ehdr *e = (Elf32_Ehdr *)ehdr;
	Elf32_Shdr *s;
	Elf32_Word entsize;
	unsigned i;
	// Find symbol table
	for (i = 0,s = (Elf32_Shdr *)((char *) e + e->e_shoff); i < e->e_shnum;
		i++,s = (Elf32_Shdr *)((char *) s + e->e_shentsize)) {
		if (s->sh_type == SHT_SYMTAB) {
			break;
		}
	}
	if (i == e->e_shnum) {
		printf("relloc: no symbol table to relocate!\n");
		return false;
	}
	entsize = s->sh_entsize;
	for (i = 0,s = (Elf32_Shdr *)((char *)e + e->e_shoff); i < e->e_shnum;
		i++,s = (Elf32_Shdr *)((char *)s + e->e_shentsize)) {
			if (s->sh_type == SHT_REL) {
				printf("Relocation segment\n");
				module_segment_t *seg;
				for (seg = mod->seg; seg; seg = seg->next) {
					if (seg->section == s->sh_info) break;
				}
				if (seg) {
					Elf32_Rel *rel,*max;
					for (rel = (Elf32_Rel *)((char *) e + s->sh_offset),
						max = rel + s->sh_size / s->sh_entsize;
						rel < max;
						rel++) {
						Elf32_Word *addr;
						Elf32_Sym *sym;
						if (seg->size < rel->r_offset) {
							printf("Relloc offset is out of segment\n");
							return false;
						}
						addr = (Elf32_Word *)((char *)seg->addr + rel->r_offset);
						sym = (Elf32_Sym *)((char *)mod->symtab + entsize * ELF32_R_SYM(rel->r_info));
						switch (ELF32_R_TYPE(rel->r_info)) {
							case R_386_32:
								*addr += sym->st_value;
								break;
							case R_386_PC32:
								*addr += (sym->st_value - (Elf32_Word)seg->addr - rel->r_offset);
								break;
						}
					}
				}
			}
	}
	return true;
}
Does i do it wrong?

Re: Module loading

Posted: Thu Nov 03, 2022 11:44 am
by nullplan
WinExperements wrote:Does i do it wrong?
Well, you are looking up the symbol table in a weird way. As I said, the section index of the symbol table you need is given in the link field of the relocation table's section header. There might be multiple symbol tables. Also, you should give an error for unknown relocation types so you know if the compiler generated something you didn't handle. Other than that, looks good so far.

Re: Module loading

Posted: Thu Nov 03, 2022 12:02 pm
by WinExperements
So why the address of kernel symbol didn't setted after loading the module, just i got #UD because the address invalid?

Re: Module loading

Posted: Thu Nov 03, 2022 12:29 pm
by iansjack
It might help to single-step through the relevant code in a debugger. Once you see what’s actually happening you have a better chance of determining the cause.

Re: Module loading

Posted: Sat Nov 05, 2022 3:08 am
by WinExperements
One thing that i found while i debug this: address of kernel symbols when loading it, setted correctly but the address didn't changed in the code(i disassemble the module entry point with GDB). Why?

Re: Module loading

Posted: Sat Nov 05, 2022 10:51 am
by devc1
That means that the address of kernel symbols isn't set correctly.


For my drivers, I create a buffer which contains the kernel mode address space (address space buffer), you should change the values in the address space buffer not in the file buffer, when you finish you just free the file buffer from memory.

The size of the virtual buffer is equal to the highest (relative) virtual address, then you copy the content of each section in that buffer using AddressSpaceBuffer + section.virtualaddress

for e.g. if RelocSection.virtualaddress = 0x5000, you should set the values in ProgramAddressSpace + 0x5000.