1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
/*
* PSn00bSDK dynamic linker
* (C) 2021-2022 spicyjpeg - MPL licensed
*/
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <elf.h>
/* Macros */
/**
* @brief Prepares for a DLL function call.
*
* @details Sets the $t9 register to the specified value (which should be a
* pointer to a DLL function obtained using DL_GetDLLSymbol()). This must be
* done prior to calling a DLL function from the main executable to ensure the
* DLL can correctly invoke the symbol resolver if necessary.
*
* This macro is not required when calling a DLL function from another DLL, as
* GCC will generate code to set $t9 appropriately.
*/
#define DL_PRE_CALL(func) \
__asm__ volatile("move $t9, %0;" :: "r"(func) : "$t9")
/* Structure and enum definitions */
typedef enum _DL_ResolveMode {
DL_LAZY = 1, // Resolve functions when they are first called (default)
DL_NOW = 2, // Resolve all symbols immediately on load
DL_FREE_ON_DESTROY = 4 // Automatically free DLL buffer when closing DLL
} 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, *malloc_ptr;
size_t size;
const uint32_t *hash;
uint32_t *got;
Elf32_Sym *symtab;
const char *strtab;
uint16_t symbol_count, first_got_symbol;
uint16_t got_local_count, got_extern_count;
} DLL;
/* Public API */
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Creates an empty symbol map in memory.
*
* @details Initializes the internal symbol hash table to contain at most the
* given number of symbols. Once this function is called, symbols can be
* registered using DL_AddMapSymbol() and then looked up using
* DL_GetMapSymbol(). The default DLL resolver will search the hash table for
* external symbols required by DLLs.
*
* This function is normally not required when loading a map file through
* DL_ParseSymbolMap(), but it can be used alongside DL_AddMapSymbol() to
* implement a custom symbol map parser.
*
* @param num_entries
* @return 0 or -1 in case of error
*
* @see DL_AddMapSymbol(), DL_GetMapSymbol()
*/
int DL_InitSymbolMap(int num_entries);
/**
* @brief Destroys the currently loaded symbol map.
*
* @details Frees the internal hash table allocated by DL_InitSymbolMap() or
* DL_ParseSymbolMap(), containing the currently loaded symbol map. Freeing the
* table manually before loading a new symbol map is normally unnecessary as it
* is done automatically, however this function can be useful to recover heap
* space once the map is no longer needed.
*/
void DL_UnloadSymbolMap(void);
/**
* @brief Adds a symbol to the currently loaded symbol map.
*
* @details Registers a new symbol (function or variable) with the given name
* and address, and adds it to the internal hash table. The symbol can then be
* looked up using DL_GetMapSymbol(). The default DLL resolver will search the
* hash table for external symbols required by DLLs.
*
* This function shall only be called after DL_InitSymbolMap() or
* DL_ParseSymbolMap() is called.
*
* @param name
* @param ptr
*
* @see DL_GetMapSymbol()
*/
void DL_AddMapSymbol(const char *name, void *ptr);
/**
* @brief Creates a symbol map in memory from a map file in text format.
*
* @details Initializes the internal symbol hash table, then parses entries
* from the provided string buffer (which may or may not be null-terminated)
* and adds each one to the table. The string buffer won't be further
* referenced and can be safely deallocated after parsing. Returns the number
* of entries successfully parsed.
*
* The string buffer shall contain one or more lines, each of which must follow
* this format:
*
* <SYMBOL_NAME> <T|R|D|B> <HEX_ADDRESS> <HEX_SIZE> [...]
*
* The "nm" tool included in the GCC toolchain can be used to generate a map
* file in the appropriate format after building the executable:
*
* mipsel-none-elf-nm -f posix -l -n executable.elf >executable.map
*
* @param ptr
* @param size
* @return Number of entries parsed, -1 in case of failure
*
* @see DL_UnloadSymbolMap(), DL_GetMapSymbol()
*/
int DL_ParseSymbolMap(const char *ptr, size_t size);
/**
* @brief Gets a pointer to a symbol in the currently loaded map by its name.
*
* @details 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, a null pointer is returned.
*
* @param name
* @return NULL or pointer to symbol
*/
void *DL_GetMapSymbol(const char *name);
/**
* @brief Sets a custom handler for resolving symbols in DLLs.
*
* @details 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_GetMapSymbol() to
* find the symbol in the currently loaded symbol map.
*
* @param callback NULL or pointer to callback function
* @return Previously set callback or NULL
*/
void *DL_SetResolveCallback(void *(*callback)(DLL *, const char *));
/**
* @brief Initializes a DLL structure.
*
* @details 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*. Metadata is written to the provided DLL struct 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 DL_DestroyDLL() on the DLL
* struct returned by this function.
*
* The third argument specifies when symbols in the DLL should be resolved.
* Setting it to DL_LAZY defers resolution of undefined functions to when they
* are first called, while DL_NOW forces all symbols to be resolved
* immediately. Either mode can be OR'd with DL_FREE_ON_DESTROY to
* automatically deallocate the provided buffer when DL_DestroyDLL() is called.
*
* If a custom resolver has been set via DL_SetResolveCallback(), it will be
* called for each symbol to resolve.
*
* @param dll
* @param ptr
* @param size
* @param mode DL_LAZY or DL_NOW, optionally with DL_FREE_ON_DESTROY
* @return Pointer to DLL structure or NULL in case of failure
*
* @see DL_DestroyDLL(), DL_GetDLLSymbol()
*/
DLL *DL_CreateDLL(DLL *dll, void *ptr, size_t size, DL_ResolveMode mode);
/**
* @brief Destroys a DLL structure.
*
* @details Destroys a loaded DLL by calling its global destructors. If the DLL
* was initialized with the DL_FREE_ON_DESTROY flag, the buffer associated with
* the DLL is also deallocated. Note that the DLL structure itself is *not*
* deallocated.
*
* @param dll
*/
void DL_DestroyDLL(DLL *dll);
/**
* @brief Gets a pointer to a symbol in a DLL by its name.
*
* @details Returns a pointer to the DLL symbol with the given name, or null if
* it can't be found. If a null pointer is passed as first argument, the
* executable itself is searched instead using the symbol map (behaving
* identically to DL_GetMapSymbol()).
*
* @param dll Pointer to DLL structure or NULL
* @param name
* @return NULL or pointer to symbol (any type)
*/
void *DL_GetDLLSymbol(const DLL *dll, const char *name);
#ifdef __cplusplus
}
#endif
|