diff options
| author | John "Lameguy" Wilbert Villamor <lameguy64@gmail.com> | 2021-08-31 13:23:20 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-08-31 13:23:20 +0800 |
| commit | ffa679d4d24b891cb59aba10946368f2ec00c391 (patch) | |
| tree | 0cf6061915ebf48acdedf6d77b0c1b76eec5b8c3 /libpsn00b/include | |
| parent | 317dc2b91d3afcdbaddb035f38611d12af161970 (diff) | |
| parent | f2fc18f82dd7900465d6ab3ae2080726d5589d39 (diff) | |
| download | psn00bsdk-ffa679d4d24b891cb59aba10946368f2ec00c391.tar.gz | |
Merge pull request #36 from spicyjpeg/dynlink
Dynamic linker, gp-relative addressing, ldscripts and more
Diffstat (limited to 'libpsn00b/include')
| -rw-r--r-- | libpsn00b/include/dlfcn.h | 176 | ||||
| -rw-r--r-- | libpsn00b/include/elf.h | 138 | ||||
| -rw-r--r-- | libpsn00b/include/psxapi.h | 1 | ||||
| -rw-r--r-- | libpsn00b/include/psxgpu.h | 2 | ||||
| -rw-r--r-- | libpsn00b/include/stdio.h | 3 |
5 files changed, 320 insertions, 0 deletions
diff --git a/libpsn00b/include/dlfcn.h b/libpsn00b/include/dlfcn.h new file mode 100644 index 0000000..5fdd343 --- /dev/null +++ b/libpsn00b/include/dlfcn.h @@ -0,0 +1,176 @@ +/* + * PSn00bSDK dynamic linker + * (C) 2021 spicyjpeg - MPL licensed + */ + +#ifndef __DLFCN_H +#define __DLFCN_H + +#include <sys/types.h> +#include <elf.h> + +/* Helper macro for setting $t9 before calling a function */ + +#define DL_CALL(func, ...) { \ + __asm__ volatile("move $t9, %0;" :: "r"(func) : "$t9"); \ + func(__VA_ARGS__); \ +} + +/* Types */ + +#define RTLD_DEFAULT ((DLL *) 0) + +typedef enum _DL_ResolveMode { + RTLD_LAZY = 1, // Resolve functions when they are first called (default) + RTLD_NOW = 2 // Resolve all symbols immediately on load +} DL_ResolveMode; + +// Members of this struct should not be accessed directly in most cases, but +// they are intentionally exposed for easier expandability. +typedef struct _DLL { + void *ptr; + void *malloc_ptr; + size_t size; + const uint32_t *hash; + uint32_t *got; + Elf32_Sym *symtab; + const char *strtab; + uint16_t symbol_count; + uint16_t got_length; +} DLL; + +/* API */ + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: rewrite these javadoc comments into proper documentation + +/** + * @brief Reads the symbol table from the provided string buffer (which may or + * may not be null-terminated), parses it and stores the parsed entries into a + * private hash table; the buffer won't be further referenced and can be safely + * deallocated after parsing. Returns the number of entries successfully parsed + * or -1 if an error occurred. + * + * This function expects the string buffer to contain one more lines, each of + * which must follow this format: + * + * <SYMBOL_NAME> <T|R|D|B> <HEX_ADDRESS> <HEX_SIZE> [DEBUG_INFO...] + * + * The "nm" tool included in the GCC toolchain can be used to generate a map + * file in the appropriate format after building the executable, by using this + * command: + * + * mipsel-unknown-elf-nm -f posix -l -n executable.elf >executable.map + * + * @param ptr + * @param size + * @return -1 or number of entries parsed + */ +int32_t DL_ParseSymbolMap(const char *ptr, size_t size); +/** + * @brief File wrapper around DL_ParseSymbolMap(). Allocates a temporary buffer + * then loads the specified map file into it (using BIOS APIs) and calls + * DL_ParseSymbolMap() to parse it. The buffer is deallocated immediately after + * parsing. + * + * @param filename Must always contain device name, e.g. "cdrom:MODULE.DLL;1" + * @return -1 or number of entries parsed + */ +int32_t DL_LoadSymbolMap(const char *filename); +/** + * @brief Frees internal buffers containing the currently loaded symbol map. + * This is automatically done before loading a new symbol map so there is no + * need to call this function in most cases, however it can still be useful to + * free up space on the heap once the symbol map is no longer needed. + */ +void DL_UnloadSymbolMap(void); +/** + * @brief Queries the currently loaded symbol map for the symbol with the given + * name and returns a pointer to it, which can then be used to directly access + * the symbol. If the symbol can't be found, null is returned instead. + * + * @param name + * @return NULL or pointer to symbol (any type) + */ +void *DL_GetSymbolByName(const char *name); +/** + * @brief Sets a custom function to be called for resolving symbols in DLLs. + * The function will be given a pointer to the current DLL and the unresolved + * symbol's name, and should return the address of the symbol in the executable + * (the dynamic linker will lock up if it returns null). Passing null instead + * of a function resets the default behavior of calling DL_GetSymbolByName() to + * find the symbol in the currently loaded symbol map. + * + * @param callback NULL or pointer to callback function + */ +void DL_SetResolveCallback(void *(*callback)(DLL *, const char *)); + +/** + * @brief Initializes a buffer holding the contents of a dynamically-loaded + * library file (compiled with the dll.ld linker script and converted to a raw + * binary) *in-place*. A new DLL struct is allocated to store metadata but, + * unlike DL_ParseSymbolMap(), the DLL's actual code, data and tables are + * referenced directly from the provided buffer. The buffer must not be moved + * or deallocated, at least not before calling dlclose() on the DLL struct + * returned by this function. + * + * The third argument specifies when symbols in the DLL should be resolved. + * Setting it to RTLD_LAZY defers resolution of undefined functions to when + * they are first called, while RTLD_DEFAULT forces all symbols to be resolved + * immediately. If a custom resolver has been set via DL_SetResolveCallback(), + * it will be called for each symbol to resolve. + * + * @param ptr + * @param size + * @param mode RTLD_LAZY or RTLD_NOW + * @return NULL or pointer to a new DLL struct + */ +DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode); +/** + * @brief File wrapper around dlinit(). Allocates a new buffer, loads the + * specified file into it (using BIOS APIs) and calls dlinit() on that. When + * calling dlclose() on a DLL loaded from a file, the file buffer is + * automatically destroyed. + * + * @param filename Must always contain device name, e.g. "cdrom:MODULE.DLL;1" + * @param mode RTLD_LAZY or RTLD_NOW + * @return NULL or pointer to a new DLL struct + */ +DLL *dlopen(const char *filename, DL_ResolveMode mode); +/** + * @brief Destroys a loaded DLL by calling its global destructors and freeing + * the buffer it's loaded in. Any pointer passed to dlclose() should no longer + * be used after the call. If the DLL was initialized in-place using dlinit(), + * dlclose() will *NOT* free the buffer initially passed to dlinit(). + * + * @param dll + */ +void dlclose(DLL *dll); +/** + * @brief Returns a pointer to the DLL symbol with the given name, or null if + * it can't be found. If null or RTLD_DEFAULT is passed as first argument, the + * executable itself is searched instead using the symbol map (behaving the + * same as DL_GetSymbolByName()). + * + * @param dll DLL struct or RTLD_DEFAULT + * @param name + * @return NULL or pointer to symbol (any type) + */ +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 + * also resets the internal error flags). + * + * @return NULL or pointer to const string + */ +const char *const dlerror(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libpsn00b/include/elf.h b/libpsn00b/include/elf.h new file mode 100644 index 0000000..b4c4408 --- /dev/null +++ b/libpsn00b/include/elf.h @@ -0,0 +1,138 @@ +/* + * PSn00bSDK dynamic linker + * (C) 2021 spicyjpeg - MPL licensed + * + * This file is used internally by the dynamic linker to parse the "header" + * (.dynamic and .dynsym sections) of dynamically-linked libraries built with + * the provided linker script. Most of it is copied from the standard Unix + * elf.h header, with the only changes being removed typedefs and #defines + * converted to enums. + */ + +#ifndef __ELF_H +#define __ELF_H + +#include <sys/types.h> + +typedef enum { + DT_NULL = 0, /* Marks end of dynamic section */ + DT_NEEDED = 1, /* Name of needed library */ + DT_PLTRELSZ = 2, /* Size in bytes of PLT relocs */ + DT_PLTGOT = 3, /* Processor defined value */ + DT_HASH = 4, /* Address of symbol hash table */ + DT_STRTAB = 5, /* Address of string table */ + DT_SYMTAB = 6, /* Address of symbol table */ + DT_RELA = 7, /* Address of Rela relocs */ + DT_RELASZ = 8, /* Total size of Rela relocs */ + DT_RELAENT = 9, /* Size of one Rela reloc */ + DT_STRSZ = 10, /* Size of string table */ + DT_SYMENT = 11, /* Size of one symbol table entry */ + DT_INIT = 12, /* Address of init function */ + DT_FINI = 13, /* Address of termination function */ + DT_SONAME = 14, /* Name of shared object */ + DT_RPATH = 15, /* Library search path (deprecated) */ + DT_SYMBOLIC = 16, /* Start symbol search here */ + DT_REL = 17, /* Address of Rel relocs */ + DT_RELSZ = 18, /* Total size of Rel relocs */ + DT_RELENT = 19, /* Size of one Rel reloc */ + DT_PLTREL = 20, /* Type of reloc in PLT */ + DT_DEBUG = 21, /* For debugging; unspecified */ + DT_TEXTREL = 22, /* Reloc might modify .text */ + DT_JMPREL = 23, /* Address of PLT relocs */ + DT_BIND_NOW = 24, /* Process relocations of object */ + DT_INIT_ARRAY = 25, /* Array with addresses of init fct */ + DT_FINI_ARRAY = 26, /* Array with addresses of fini fct */ + DT_INIT_ARRAYSZ = 27, /* Size in bytes of DT_INIT_ARRAY */ + DT_FINI_ARRAYSZ = 28, /* Size in bytes of DT_FINI_ARRAY */ + DT_RUNPATH = 29, /* Library search path */ + DT_FLAGS = 30, /* Flags for the object being loaded */ + DT_ENCODING = 32, /* Start of encoded range */ + DT_PREINIT_ARRAY = 32, /* Array with addresses of preinit fct*/ + DT_PREINIT_ARRAYSZ = 33, /* size in bytes of DT_PREINIT_ARRAY */ + DT_SYMTAB_SHNDX = 34, /* Address of SYMTAB_SHNDX section */ + DT_NUM = 35, /* Number used */ + DT_LOOS = 0x6000000d, /* Start of OS-specific */ + DT_HIOS = 0x6ffff000, /* End of OS-specific */ + DT_LOPROC = 0x70000000, /* Start of processor-specific */ + DT_HIPROC = 0x7fffffff, /* End of processor-specific */ + + DT_MIPS_RLD_VERSION = 0x70000001, + DT_MIPS_FLAGS = 0x70000005, + DT_MIPS_BASE_ADDRESS = 0x70000006, + DT_MIPS_LOCAL_GOTNO = 0x7000000a, + DT_MIPS_SYMTABNO = 0x70000011, + DT_MIPS_UNREFEXTNO = 0x70000012, + DT_MIPS_GOTSYM = 0x70000013, + DT_MIPS_HIPAGENO = 0x70000014 +} Elf32_d_tag; + +typedef enum { + RHF_NONE = 0, /* No flags */ + RHF_QUICKSTART = 1, /* Use quickstart */ + RHF_NOTPOT = 2, /* Hash size not power of 2 */ + RHF_NO_LIBRARY_REPLACEMENT = 4 /* Ignore LD_LIBRARY_PATH */ +} Elf32_d_MIPS_FLAGS; + +typedef struct { + Elf32_d_tag d_tag; /* Dynamic entry type */ + union { + uint32_t d_val; /* Integer value */ + void *d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct { + uint32_t st_name; /* Symbol name (string tbl index) */ + void *st_value; /* Symbol value */ + size_t st_size; /* Symbol size */ + uint8_t st_info; /* Symbol type and binding */ + uint8_t st_other; /* Symbol visibility */ + uint16_t st_shndx; /* Section index */ +} Elf32_Sym; + +#define ELF32_ST_BIND(val) ((Elf32_st_bind) (((uint8_t) (val)) >> 4)) +#define ELF32_ST_TYPE(val) ((Elf32_st_type) ((val) & 0xf)) +#define ELF32_ST_INFO(bind, type) ((((uint8_t) (bind)) << 4) + (((uint8_t) (type)) & 0xf)) + +typedef enum { + STB_LOCAL = 0, /* Local symbol */ + STB_GLOBAL = 1, /* Global symbol */ + STB_WEAK = 2, /* Weak symbol */ + STB_NUM = 3, /* Number of defined types. */ + STB_LOOS = 10, /* Start of OS-specific */ + STB_GNU_UNIQUE = 10, /* Unique symbol. */ + STB_HIOS = 12, /* End of OS-specific */ + STB_LOPROC = 13, /* Start of processor-specific */ + STB_HIPROC = 15 /* End of processor-specific */ +} Elf32_st_bind; + +typedef enum { + STT_NOTYPE = 0, /* Symbol type is unspecified */ + STT_OBJECT = 1, /* Symbol is a data object */ + STT_FUNC = 2, /* Symbol is a code object */ + STT_SECTION = 3, /* Symbol associated with a section */ + STT_FILE = 4, /* Symbol's name is file name */ + STT_COMMON = 5, /* Symbol is a common data object */ + STT_TLS = 6, /* Symbol is thread-local data object*/ + STT_NUM = 7, /* Number of defined types. */ + STT_LOOS = 10, /* Start of OS-specific */ + STT_GNU_IFUNC = 10, /* Symbol is indirect code object */ + STT_HIOS = 12, /* End of OS-specific */ + STT_LOPROC = 13, /* Start of processor-specific */ + STT_HIPROC = 15 /* End of processor-specific */ +} Elf32_st_type; + +// If you need to add more constants, you may use the following Python snippet +// to quickly convert #defines to enums: +/* +import re +t = """<paste #defines here>""" +t = re.sub( + r"(0x[0-9a-f]+|0b[01]+|[0-9]+)", + lambda m: f"= {m.group(1)},", + t.replace("#define ", "\t").replace("#define\t", "\t") +) +print("typedef enum {\n" + t + "\n} NAME;") +*/ + +#endif diff --git a/libpsn00b/include/psxapi.h b/libpsn00b/include/psxapi.h index c366702..9e92568 100644 --- a/libpsn00b/include/psxapi.h +++ b/libpsn00b/include/psxapi.h @@ -180,6 +180,7 @@ void ChangeClearRCnt(int t, int m); // Executable functions int Exec(struct EXEC *exec, int argc, char **argv); +void FlushCache(void); void _boot(void); diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h index ddb4522..f50b841 100644 --- a/libpsn00b/include/psxgpu.h +++ b/libpsn00b/include/psxgpu.h @@ -1,6 +1,8 @@ #ifndef __PSXGPU_H #define __PSXGPU_H +#include <sys/types.h> + // Low-level display parameters for DISPENV_RAW. A leftover from prototyping #define DISP_WIDTH_256 0 #define DISP_WIDTH_320 1 diff --git a/libpsn00b/include/stdio.h b/libpsn00b/include/stdio.h index cf1909d..97a2f5a 100644 --- a/libpsn00b/include/stdio.h +++ b/libpsn00b/include/stdio.h @@ -56,6 +56,9 @@ int vsprintf(char *string, const char *fmt, va_list ap); int sprintf(char *string, const char *fmt, ...); int snprintf(char *string, unsigned int size, const char *fmt, ...); +int vsscanf(const char *str, const char *format, va_list ap); +int sscanf(const char *str, const char *fmt, ...); + #ifdef __cplusplus } #endif |
