diff options
| author | spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> | 2021-10-19 22:30:21 +0200 |
|---|---|---|
| committer | spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> | 2021-10-20 16:57:14 +0200 |
| commit | 4e0d5bceb24042a6d31c76958ce2c6157369ac68 (patch) | |
| tree | 20d3110363ab468e99502337c0a46852662bb365 | |
| parent | 7a2e6dd9550376f7a0511e73b33194170d712384 (diff) | |
| download | psn00bsdk-4e0d5bceb24042a6d31c76958ce2c6157369ac68.tar.gz | |
libc build fix, replaced dlerror() strings with enum
| -rw-r--r-- | examples/system/dynlink/main.c | 10 | ||||
| -rw-r--r-- | libpsn00b/CMakeLists.txt | 14 | ||||
| -rw-r--r-- | libpsn00b/include/dlfcn.h | 25 | ||||
| -rw-r--r-- | libpsn00b/psxetc/dl.c | 206 |
4 files changed, 148 insertions, 107 deletions
diff --git a/examples/system/dynlink/main.c b/examples/system/dynlink/main.c index 70314da..33f6f44 100644 --- a/examples/system/dynlink/main.c +++ b/examples/system/dynlink/main.c @@ -62,7 +62,9 @@ const void *const DO_NOT_STRIP[] __attribute__((section(".dummy"))) = { &InitGeom, &RotMatrix, &TransMatrix, - &MulMatrix0 + &MulMatrix0, + &GetTimInfo, + &LoadImage }; static const char *const DLL_FILENAMES[] = { @@ -141,7 +143,7 @@ void load_dll(const char *filename) { dll = dlopen(filename, RTLD_LAZY); if (!dll) - SHOW_ERROR("FAILED TO LOAD %s\n%s\n", filename, dlerror()); + SHOW_ERROR("FAILED TO LOAD %s\nERROR=%d\n", filename, (int32_t) dlerror()); dll_api.init = dlsym(dll, "init"); dll_api.render = dlsym(dll, "render"); @@ -168,12 +170,12 @@ int main(int argc, const char* argv[]) { SHOW_STATUS("LOADING SYMBOL MAP\n"); if (!DL_LoadSymbolMap("cdrom:MAIN.MAP;1")) - SHOW_ERROR("FAILED TO LOAD SYMBOL MAP\n%s\n", dlerror()); + SHOW_ERROR("FAILED TO LOAD SYMBOL MAP\nERROR=%d\n", (int32_t) dlerror()); // Try to obtain a reference to a local function. void (*_display)() = DL_GetSymbolByName("display"); if (!_display) - SHOW_ERROR("FAILED TO LOOK UP LOCAL FUNCTION\n%s\n", dlerror()); + SHOW_ERROR("FAILED TO LOOK UP LOCAL FUNCTION\nERROR=%d\n", (int32_t) dlerror()); printf("Symbol map test, display() @ %08x\n", _display); diff --git a/libpsn00b/CMakeLists.txt b/libpsn00b/CMakeLists.txt index bfe27c6..083208a 100644 --- a/libpsn00b/CMakeLists.txt +++ b/libpsn00b/CMakeLists.txt @@ -40,13 +40,19 @@ foreach(_library IN LISTS PSN00BSDK_LIBRARIES) psn00bsdk_add_library(${_library} STATIC ${_sources}) endforeach() -# Copy libgcc.a and merge the PSn00bSDK libc into it after building. +# Extract libgcc's contents and merge them into libc after building. +# Unfortunately glob expressions won't work on Windows, so we have to manually +# enumerate the contents of libgcc and save the list to a temporary file (as it +# is too long to be passed directly on the command line). This is actually the +# most reliable way I found to do this (I tried $<TARGET_OBJECTS> to no avail). add_custom_command( TARGET c POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${PSN00BSDK_LIBGCC} $<TARGET_FILE:c> + COMMAND ${CMAKE_AR} t ${PSN00BSDK_LIBGCC} $<ANGLE-R>_libgcc.txt + COMMAND ${CMAKE_AR} x ${PSN00BSDK_LIBGCC} + COMMAND ${CMAKE_AR} q $<TARGET_FILE:c> \@_libgcc.txt + COMMAND ${CMAKE_AR} s $<TARGET_FILE:c> #COMMAND ${CMAKE_AR} rsuU $<TARGET_FILE:c> *.o - COMMAND ${CMAKE_AR} rsuU $<TARGET_FILE:c> $<JOIN:"$<TARGET_OBJECTS:c>"," "> - COMMENT "Merging SDK libc contents into libgcc" + COMMENT "Merging libgcc contents into SDK libc" ) ## Installation diff --git a/libpsn00b/include/dlfcn.h b/libpsn00b/include/dlfcn.h index 0c51821..b3a5cec 100644 --- a/libpsn00b/include/dlfcn.h +++ b/libpsn00b/include/dlfcn.h @@ -20,6 +20,23 @@ #define RTLD_DEFAULT ((DLL *) 0) +typedef enum _DL_Error { + RTLD_E_NONE = 0, // No error + RTLD_E_FILE_OPEN = 1, // Unable to find or open file + RTLD_E_FILE_ALLOC = 2, // Unable to allocate buffer to load file into + RTLD_E_FILE_READ = 3, // Failed to read file + RTLD_E_NO_MAP = 4, // No symbol map has been loaded yet + RTLD_E_MAP_ALLOC = 5, // Unable to allocate symbol map structures + RTLD_E_NO_SYMBOLS = 6, // No symbols found in symbol map + RTLD_E_DLL_NULL = 7, // Unable to initialize DLL from null pointer + RTLD_E_DLL_ALLOC = 8, // Unable to allocate DLL metadata structures + RTLD_E_DLL_FORMAT = 9, // Unsupported DLL type or format + RTLD_E_NO_FILE_API = 10, // psxetc has been built without file support + RTLD_E_MAP_SYMBOL = 11, // Symbol not found in symbol map + RTLD_E_DLL_SYMBOL = 12, // Symbol not found in DLL + RTLD_E_HASH_LOOKUP = 13 // Hash table lookup failed due to internal error +} DL_Error; + typedef enum _DL_ResolveMode { RTLD_LAZY = 1, // Resolve functions when they are first called (default) RTLD_NOW = 2 // Resolve all symbols immediately on load @@ -161,13 +178,13 @@ void dlclose(DLL *dll); */ void *dlsym(DLL *dll, const char *name); /** - * @brief Returns a string describing the last error that occurred, or null if - * no error has occurred since the last call to dlerror() (i.e. calling this + * @brief Returns a code describing the last error that occurred, or DL_E_NONE + * if no error has occurred since the last call to dlerror() (i.e. calling this * also resets the internal error flags). * - * @return NULL or pointer to const string + * @return NULL or member of DL_Error enum */ -const char *const dlerror(void); +DL_Error dlerror(void); #ifdef __cplusplus } diff --git a/libpsn00b/psxetc/dl.c b/libpsn00b/psxetc/dl.c index e405efc..586e7e1 100644 --- a/libpsn00b/psxetc/dl.c +++ b/libpsn00b/psxetc/dl.c @@ -48,42 +48,19 @@ typedef struct { void *ptr; } MapEntry; -typedef enum { - ERR_NONE, - ERR_FILE, - ERR_FILE_MALLOC, - ERR_FILE_READ, - ERR_NO_MAP, - ERR_MAP_MALLOC, - ERR_NO_SYMBOLS, - ERR_DLL_NULL, - ERR_DLL_MALLOC, - ERR_DLL_FORMAT, - ERR_NO_FILE_API, - ERR_MAP_SYMBOL, - ERR_DLL_SYMBOL -} ErrorCode; +typedef struct { + uint32_t nbucket; + uint32_t nchain; + + MapEntry *entries; + uint32_t *bucket; + uint32_t *chain; +} SymbolMap; /* Data */ -static const char *const DL_ERROR_MESSAGES[] = { - "Unable to find file", - "Unable to allocate buffer to load file into", - "Failed to read file", - "No symbol map has been loaded yet", - "Unable to allocate symbol map structures", - "No symbols found in symbol map", - "Unable to initialize DLL from null pointer", - "Unable to allocate DLL metadata structures", - "Unsupported DLL type or format", - "psxetc has been built without file support", - "Symbol not found in symbol map", - "Symbol not found in DLL" -}; - -static ErrorCode _error_code = ERR_NONE; -static uint32_t *_map_hash_table = 0; -static MapEntry *_map_entry_table = 0; +static DL_Error _error_code = RTLD_E_NONE; +static SymbolMap _symbol_map; // Accessed by _dl_resolve_helper, stores the pointer to the current resolver // function. Can be changed using DL_SetResolveCallback(). @@ -93,18 +70,14 @@ void *(*_dl_resolve_callback)(DLL *, const char *) = 0; #ifdef DEBUG #define _LOG(...) printf(__VA_ARGS__) -#define _ERROR(code, ret) { \ - _LOG("psxetc: ERROR! %s\n", DL_ERROR_MESSAGES[code - 1]); \ - _error_code = code; \ - return ret; \ -} #else #define _LOG(...) +#endif + #define _ERROR(code, ret) { \ _error_code = code; \ return ret; \ } -#endif void _dl_resolve_wrapper(void); @@ -161,8 +134,10 @@ static uint32_t _elf_hash(const char *str) { #ifdef USE_FILE_API static uint8_t *_load_file(const char *filename, size_t *size_output) { int32_t fd = open(filename, 1); - if (fd < 0) - _ERROR(ERR_FILE, 0); + if (fd < 0) { + _LOG("psxetc: Can't open %s, error = %d\n", filename, fd); + _ERROR(RTLD_E_FILE_OPEN, 0); + } // Extract file size from the file's associated control block. // https://problemkaputt.de/psx-spx.htm#biosmemorymap @@ -170,8 +145,10 @@ static uint8_t *_load_file(const char *filename, size_t *size_output) { size_t size = fcb[fd].filesize; uint8_t *buffer = malloc(size); - if (!buffer) - _ERROR(ERR_FILE_MALLOC, 0); + if (!buffer) { + _LOG("psxetc: Unable to allocate %d bytes for %s\n", size, filename); + _ERROR(RTLD_E_FILE_ALLOC, 0); + } _LOG("psxetc: Loading %s (%d bytes)..", filename, size); @@ -181,7 +158,9 @@ static uint8_t *_load_file(const char *filename, size_t *size_output) { if (length <= 0) { close(fd); free(buffer); - _ERROR(ERR_FILE_READ, 0); + + _LOG("failed, error = %d\n", length); + _ERROR(RTLD_E_FILE_READ, 0); } _LOG("."); @@ -213,20 +192,29 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size) { // TODO: find a way to calculate the optimal number of hash table "buckets" // in order to minimize hash table size - uint32_t nbucket = entries; - _LOG("psxetc: Predicted %d entries, %d hash buckets\n", entries, nbucket); + _symbol_map.nbucket = entries; + _symbol_map.nchain = entries; + _LOG( + "psxetc: Allocating nbucket = %d, nchain = %d\n", + _symbol_map.nbucket, + entries + ); // Allocate an entry table to store parsed symbols in, and an associated // hash table (same format as .hash section, with 8-byte header). - _map_hash_table = malloc(sizeof(uint32_t) * (2 + nbucket + entries)); - _map_entry_table = malloc(sizeof(MapEntry) * entries); - if (!_map_hash_table || !_map_entry_table) - _ERROR(ERR_MAP_MALLOC, -1); + _symbol_map.entries = malloc(sizeof(MapEntry) * entries); + _symbol_map.bucket = malloc(sizeof(uint32_t) * _symbol_map.nbucket); + _symbol_map.chain = malloc(sizeof(uint32_t) * entries); - _map_hash_table[0] = nbucket; - _map_hash_table[1] = entries; - for (uint32_t i = 0; i < (nbucket + entries); i++) - _map_hash_table[2 + i] = 0xffffffff; + if (!_symbol_map.entries || !_symbol_map.bucket || !_symbol_map.chain) { + _LOG("psxetc: Unable to allocate symbol map table\n"); + _ERROR(RTLD_E_MAP_ALLOC, -1); + } + + for (uint32_t i = 0; i < _symbol_map.nbucket; i++) + _symbol_map.bucket[i] = 0xffffffff; + for (uint32_t i = 0; i < entries; i++) + _symbol_map.chain[i] = 0xffffffff; // Go again through the symbol map and fill in the hash table. uint32_t index = 0; @@ -254,7 +242,7 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size) { void *address = (void *) address64; char _type = toupper(type_string[0]); uint32_t hash = _elf_hash(name); - uint32_t hash_mod = hash % nbucket; + uint32_t hash_mod = hash % _symbol_map.nbucket; if (address && ( (_type == 'T') || // .text @@ -270,15 +258,15 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size) { name ); - MapEntry *entry = &(_map_entry_table[index]); + MapEntry *entry = &(_symbol_map.entries[index]); entry->hash = hash; entry->ptr = address; // Append a reference to the entry to the hash table's chain // for the current hash_mod. I can't explain this properly. - uint32_t *hash_entry = &(_map_hash_table[2 + hash_mod]); + uint32_t *hash_entry = &(_symbol_map.bucket[hash_mod]); while (*hash_entry != 0xffffffff) - hash_entry = &(_map_hash_table[2 + nbucket + *hash_entry]); + hash_entry = &(_symbol_map.chain[*hash_entry]); *hash_entry = index; index++; @@ -291,9 +279,9 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size) { pos++; } - _LOG("psxetc: Parsed %d symbols from map\n", entries); + _LOG("psxetc: Parsed %d symbols\n", entries); if (!entries) - _ERROR(ERR_NO_SYMBOLS, -1); + _ERROR(RTLD_E_NO_SYMBOLS, -1); return entries; } @@ -310,42 +298,54 @@ int32_t DL_LoadSymbolMap(const char *filename) { return entries; #else - _ERROR(ERR_NO_FILE_API, -1); + _ERROR(RTLD_E_NO_FILE_API, -1); #endif } void DL_UnloadSymbolMap(void) { - if (!_map_hash_table) + if (!_symbol_map.entries) return; - free(_map_hash_table); - free(_map_entry_table); - _map_hash_table = 0; + free(_symbol_map.entries); + free(_symbol_map.bucket); + free(_symbol_map.chain); + _symbol_map.entries = 0; } void *DL_GetSymbolByName(const char *name) { - if (!_map_hash_table) - _ERROR(ERR_NO_MAP, 0); + if (!_symbol_map.entries) { + _LOG("psxetc: Attempted lookup with no map loaded\n"); + _ERROR(RTLD_E_NO_MAP, 0); + } // https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-48031.html - uint32_t nbucket = _map_hash_table[0]; uint32_t hash = _elf_hash(name); - uint32_t hash_mod = hash % nbucket; + uint32_t hash_mod = hash % _symbol_map.nbucket; // Go through the hash table's chain until the symbol hash matches the one // calculated. - for (uint32_t i = _map_hash_table[2 + hash_mod]; i;) { - MapEntry *entry = &(_map_entry_table[i]); + for (uint32_t i = _symbol_map.bucket[hash_mod]; i != 0xffffffff;) { + if (i >= _symbol_map.nchain) { + _LOG( + "psxetc: GetSymbolByName() index out of bounds (i = %d, n = %d)\n", + i, + _symbol_map.nchain + ); + _ERROR(RTLD_E_HASH_LOOKUP, 0); + } + + MapEntry *entry = &(_symbol_map.entries[i]); if (hash == entry->hash) { _LOG("psxetc: Map lookup [%s = %08x]\n", name, entry->ptr); return entry->ptr; } - i = _map_hash_table[2 + nbucket + i]; + i = _symbol_map.chain[i]; } - _ERROR(ERR_MAP_SYMBOL, 0); + _LOG("psxetc: Map lookup [%s not found]\n", name); + _ERROR(RTLD_E_MAP_SYMBOL, 0); } void DL_SetResolveCallback(void *(*callback)(DLL *, const char *)) { @@ -356,11 +356,13 @@ void DL_SetResolveCallback(void *(*callback)(DLL *, const char *)) { DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { if (!ptr) - _ERROR(ERR_DLL_NULL, 0); + _ERROR(RTLD_E_DLL_NULL, 0); DLL *dll = malloc(sizeof(DLL)); - if (!dll) - _ERROR(ERR_DLL_MALLOC, 0); + if (!dll) { + _LOG("psxetc: Unable to allocate DLL struct\n"); + _ERROR(RTLD_E_DLL_ALLOC, 0); + } dll->ptr = ptr; dll->malloc_ptr = 0; @@ -417,7 +419,9 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { // Only 16-byte symbol table entries are supported. if (dyn->d_un.d_val != sizeof(Elf32_Sym)) { free(dll); - _ERROR(ERR_DLL_FORMAT, 0); + + _LOG("psxetc: Invalid symtab entry size %d\n", dyn->d_un.d_val); + _ERROR(RTLD_E_DLL_FORMAT, 0); } break; @@ -428,7 +432,9 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { // Versions other than 1 are unsupported (do they even exist?). if (dyn->d_un.d_val != 1) { free(dll); - _ERROR(ERR_DLL_FORMAT, 0); + + _LOG("psxetc: Invalid DLL version %d\n", dyn->d_un.d_val); + _ERROR(RTLD_E_DLL_FORMAT, 0); } break; @@ -439,7 +445,9 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { // Shortcut pointers (whatever they are) are not supported. if (dyn->d_un.d_val & RHF_QUICKSTART) { free(dll); - _ERROR(ERR_DLL_FORMAT, 0); + + _LOG("psxetc: Invalid flags\n"); + _ERROR(RTLD_E_DLL_FORMAT, 0); } break; @@ -458,7 +466,9 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { // be easy enough to support them, but why? if (dyn->d_un.d_val) { free(dll); - _ERROR(ERR_DLL_FORMAT, 0); + + _LOG("psxetc: Invalid base address %08x\n", dyn->d_un.d_val); + _ERROR(RTLD_E_DLL_FORMAT, 0); } break; @@ -558,7 +568,7 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { if (!dll->got[2 + j]) { free(dll); - _ERROR(ERR_MAP_SYMBOL, 0); + _ERROR(RTLD_E_MAP_SYMBOL, 0); } } @@ -600,7 +610,7 @@ DLL *dlopen(const char *filename, DL_ResolveMode mode) { return dll; #else - _ERROR(ERR_NO_FILE_API, 0); + _ERROR(RTLD_E_NO_FILE_API, 0); #endif } @@ -633,13 +643,21 @@ void *dlsym(DLL *dll, const char *name) { //return _dl_resolve_callback(RTLD_DEFAULT, name); // https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-48031.html - const uint32_t *hash_table = dll->hash; - uint32_t nbucket = hash_table[0]; - uint32_t hash_mod = _elf_hash(name) % nbucket; + uint32_t nbucket = dll->hash[0]; + uint32_t nchain = dll->hash[1]; + const uint32_t *bucket = &(dll->hash[2]); + const uint32_t *chain = &(dll->hash[2 + nbucket]); + + uint32_t hash_mod = _elf_hash(name) % nbucket; // Go through the hash table's chain until the symbol name matches the one // provided. - for (uint32_t i = hash_table[2 + hash_mod]; i;) { + for (uint32_t i = bucket[hash_mod]; i != 0xffffffff;) { + if (i >= nchain) { + _LOG("psxetc: dlsym() index out of bounds (i = %d, n = %d)\n", i, nchain); + _ERROR(RTLD_E_HASH_LOOKUP, 0); + } + Elf32_Sym *sym = &(dll->symtab[i]); const char *_name = &(dll->strtab[sym->st_name]); @@ -648,18 +666,16 @@ void *dlsym(DLL *dll, const char *name) { return sym->st_value; } - i = hash_table[2 + nbucket + i]; + i = chain[i]; } - _ERROR(ERR_DLL_SYMBOL, 0); + _LOG("psxetc: DLL lookup [%s not found]\n", name); + _ERROR(RTLD_E_DLL_SYMBOL, 0); } -const char *const dlerror(void) { - uint32_t last = _error_code; - _error_code = ERR_NONE; - - if (last) - return DL_ERROR_MESSAGES[last - 1]; +DL_Error dlerror(void) { + DL_Error last = _error_code; + _error_code = RTLD_E_NONE; - return 0; + return last; } |
