|
PatchworkOS
28a9544
A non-POSIX operating system.
|
Kernel Symbol Resolution and Management. More...
Data Structures | |
| struct | symbol_group_t |
| struct | symbol_name_t |
| Symbol name mapping structure. More... | |
| struct | symbol_addr_t |
| Symbol address mapping structure. More... | |
| struct | symbol_info_t |
| Symbol information structure. More... | |
Macros | |
| #define | SYMBOL_MAX_NAME MAP_KEY_MAX_LENGTH |
| Maximum length of a symbol name. | |
Typedefs | |
| typedef uint64_t | symbol_group_id_t |
| Symbol group identifier type. | |
Functions | |
| symbol_group_id_t | symbol_generate_group_id (void) |
| Generate a unique symbol group identifier. | |
| uint64_t | symbol_add (const char *name, void *addr, symbol_group_id_t groupId, Elf64_Symbol_Binding binding, Elf64_Symbol_Type type) |
| Add a symbol to the kernel symbol table. | |
| void | symbol_remove_group (symbol_group_id_t groupId) |
| Remove all symbols from the kernel symbol table in the given group. | |
| uint64_t | symbol_resolve_addr (symbol_info_t *outSymbol, void *addr) |
| Resolve a symbol by address. | |
| uint64_t | symbol_resolve_name (symbol_info_t *outSymbol, const char *name) |
| Resolve a symbol by name. | |
Kernel Symbol Resolution and Management.
All binary files are made up of "symbols", a name associated with an address in the binary, which includes the kernel. These symbols are usually stored in the binary file of whatever binary we are dealing with, usually the only purpose of these symbols is linking and debugging.
We can take advantage of these symbols to resolve symbol names to addresses ("kmain" -> 0xXXXXXXXX) and addresses to symbol names (0xXXXXXXXX -> "kmain") at runtime. This is not only massively useful for debugging and logging, but vital for implementing kernel modules, as the kernel effectively acts as a "runtime linker" for the kernel module binaries, resolving any kernel symbols (which are stored in the module binary by its name since it cant know the address beforehand) to their actual addresses in the kernel so that the module can call into the kernel and of course vice versa. We can also use this to resolve symbols between modules.
In the end we have a large structure of all currently loaded symbols in the kernel or modules, and we can search this structure by name or by address.
The kernel stores symbols using three main structures, which when combined form a slightly over optimized way to retrieve symbols by name or address and to easily remove symbols when a module is unloaded.
The structures are:
symbol_group_t), used to group symbols for easy removal later.symbol_name_t), used to resolve names to addresses.symbol_addr_t), used to resolve addresses to names using binary search.These structures form a kind of circular graph, where from a group we can retrieve the names, from the names we can retrieve the addresses and from the addresses we can retrieve the group again. Its also possible to go from a address to its name using the CONTAINER_OF macro.
Note that we cant use a map for the addresses as we need to be able to find non-exact matches when resolving an address. If a address inside a function is provided we still want to be able to resolve it to the function name, this is done by finding the closest symbol with an address less than or equal to the provided address.
| #define SYMBOL_MAX_NAME MAP_KEY_MAX_LENGTH |
| uint64_t symbol_add | ( | const char * | name, |
| void * | addr, | ||
| symbol_group_id_t | groupId, | ||
| Elf64_Symbol_Binding | binding, | ||
| Elf64_Symbol_Type | type | ||
| ) |
Add a symbol to the kernel symbol table.
Symbols of binding STB_GLOBAL must have unique names but can have duplicated addresses, symbols of other bindings can be duplicated in name, address or both.
If the symbol is not of type STT_OBJECT or STT_FUNC the function is a no-op and returns success.
| name | The name of the symbol. |
| addr | The address of the symbol. |
| groupId | The group identifier of the symbol. |
| binding | The binding of the symbol, specifies visibility and linkage. |
| type | The type of the symbol, specifies what the symbol represents. |
0. On failure, ERR and errno is set. Definition at line 165 of file symbol.c.
References symbol_name_t::addrs, CONTAINER_OF_SAFE, EEXIST, EINVAL, ERR, errno, free(), symbol_name_t::groupEntry, groupMap, symbol_group_t::id, list_entry_init(), list_init(), list_push_back(), list_remove(), lock, LOG_DEBUG, malloc(), map_entry_init(), map_get(), map_insert(), map_key_string(), map_key_uint64(), map_remove(), symbol_group_t::mapEntry, symbol_name_t::mapEntry, symbol_name_t::name, nameMap, symbol_group_t::names, NULL, RWLOCK_WRITE_SCOPE, STB_GLOBAL, strerror(), strncpy_s(), STT_FUNC, STT_OBJECT, symbol_insert_address(), SYMBOL_MAX_NAME, and symbol_resolve_name_unlocked().
Referenced by module_init_fake_kernel_module(), and module_load_and_relocate_elf().
| symbol_group_id_t symbol_generate_group_id | ( | void | ) |
Generate a unique symbol group identifier.
All identifiers are generated sequentially.
Definition at line 24 of file symbol.c.
References _Atomic, atomic_fetch_add_explicit, ATOMIC_VAR_INIT, and memory_order_relaxed.
| void symbol_remove_group | ( | symbol_group_id_t | groupId | ) |
Remove all symbols from the kernel symbol table in the given group.
| groupId | The group identifier of the symbols to remove. |
Definition at line 278 of file symbol.c.
References addrAmount, addrArray, addrCapacity, symbol_name_t::addrs, CONTAINER_OF, CONTAINER_OF_SAFE, free(), groupMap, list_is_empty(), list_pop_first(), lock, map_get(), map_key_uint64(), map_remove(), symbol_group_t::mapEntry, symbol_name_t::mapEntry, nameMap, symbol_group_t::names, NULL, realloc(), and RWLOCK_WRITE_SCOPE.
Referenced by module_free().
| uint64_t symbol_resolve_addr | ( | symbol_info_t * | outSymbol, |
| void * | addr | ||
| ) |
Resolve a symbol by address.
The resolved symbol is the closest symbol with an address less than or equal to the given address. The outSymbol->addr will be the address of the symbol, not the given address.
If multiple symbols exist at the same address, one of them will be returned, but which one is undefined. Dont rely on this behaviour being predictable.
| outSymbol | Output pointer to store the resolved symbol information. |
| addr | The address of the symbol to resolve. |
0. On failure, ERR and errno is set. Definition at line 338 of file symbol.c.
References lock, RWLOCK_READ_SCOPE, and symbol_resolve_addr_unlocked().
Referenced by panic_print_trace_address(), and panic_registers().
| uint64_t symbol_resolve_name | ( | symbol_info_t * | outSymbol, |
| const char * | name | ||
| ) |
Resolve a symbol by name.
| outSymbol | Output pointer to store the resolved symbol information. |
| name | The name of the symbol to resolve. |
0. On failure, ERR and errno is set. Definition at line 344 of file symbol.c.
References lock, RWLOCK_READ_SCOPE, and symbol_resolve_name_unlocked().
Referenced by module_resolve_callback().