2 * gmodule.c: dl* functions, glib style
5 * Gonzalo Paniagua Javier (gonzalo@novell.com)
6 * Jonathan Chambers (joncham@gmail.com)
7 * Robert Jordan (robertj@gmx.net)
8 * Calvin Buckley (calvin@cmpct.info)
10 * (C) 2006 Novell, Inc.
11 * (C) 2006 Jonathan Chambers
12 * (C) 2019 Calvin Buckley
14 * Permission is hereby granted, free of charge, to any person obtaining
15 * a copy of this software and associated documentation files (the
16 * "Software"), to deal in the Software without restriction, including
17 * without limitation the rights to use, copy, modify, merge, publish,
18 * distribute, sublicense, and/or sell copies of the Software, and to
19 * permit persons to whom the Software is furnished to do so, subject to
20 * the following conditions:
22 * The above copyright notice and this permission notice shall be
23 * included in all copies or substantial portions of the Software.
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 /* In case by some miracle, IBM implements this */
35 #if defined(_AIX) && !defined(HAVE_DLADDR)
44 #include <sys/errno.h>
47 /* AIX specific headers for loadquery and traceback structure */
49 #include <sys/debug.h>
51 /* library filename + ( + member file name + ) + NUL */
52 #define AIX_PRINTED_LIB_LEN ((PATH_MAX * 2) + 3)
55 * The structure that holds information for dladdr. Unfortunately, on AIX,
56 * the information returned by loadquery lives in an allocated buffer, so it
57 * should be freed when no longer needed. Note that sname /is/ still constant
58 * (it points to the traceback info in the image), so don't free it.
60 typedef struct _g_dl_info
{
63 const char* dli_sname
;
68 * Gets the base address and name of a symbol.
70 * This uses the traceback table at the function epilogue to get the base
71 * address and the name of a symbol. As such, this means that the input must
72 * be a word-aligned address within the text section.
74 * The way to support non-text (data/bss/whatever) would be to use an XCOFF
75 * parser on the image loaded in memory and snarf its symbol table. However,
76 * that is much more complex, and presumably, most addresses passed would be
77 * code in the text section anyways (I hope so, anyways...) Unfortunately,
78 * this does mean that function descriptors, which live in data, won't work.
79 * The traceback approach actually works with JITted code too, provided it
80 * could be emitted with XCOFF traceback...
83 _g_sym_from_tb(void **sbase
, const char **sname
, void *where
) {
84 unsigned int *s
= (unsigned int*)where
;
86 /* look for zero word (invalid op) that begins epilogue */
89 /* We're on a zero word now, seek after the traceback table. */
90 struct tbtable_short
*tb
= (struct tbtable_short
*)(s
+ 1);
91 /* The extended traceback is variable length, so more seeking. */
92 char *ext
= (char*)(tb
+ 1);
93 /* Skip a lot of cruft, in order according to the ext "structure". */
94 if (tb
->fixedparms
|| tb
->floatparms
) {
95 ext
+= sizeof(unsigned int);
99 void *start
= (char*)s
- *((unsigned int*)ext
);
100 ext
+= sizeof (unsigned int);
101 *sbase
= (void*)start
;
104 * Can we go backwards instead until we hit a null word,
105 * that /precedes/ the block of code?
106 * Does the XCOFF/traceback format allow for that?
108 *sbase
= NULL
; /* NULL base address as a sentinel */
115 int ctlnum
= (*(int*)ext
);
116 ext
+= sizeof(int) + (sizeof(int) * ctlnum
);
118 if (tb
->name_present
) {
120 * The 16-bit name length is here, but the name seems to
121 * include a NUL, so we don't reallocate it, and instead
122 * just point to its location in memory.
124 ext
+= sizeof(short);
132 * Look for the base address and name of both a symbol and the corresponding
133 * executable in memory. This is a simplistic reimplementation for AIX.
135 * Returns 1 on failure and 0 on success. "s" is the address of the symbol,
136 * and "i" points to a Dl_info structure to fill. Note that i.dli_fname is
137 * not const, and should be freed.
140 _g_dladdr(void* s
, _g_Dl_info
* i
) {
142 * Use stack instead of heap because malloc may be messed up.
143 * Init returned structure members to clear out any garbage.
145 char *buf
= (char*)g_alloca(10000);
150 int r
= loadquery (L_GETINFO
, buf
, 10000);
154 /* The loader info structures are also a linked list. */
155 struct ld_info
*cur
= (struct ld_info
*) buf
;
158 * Check in text and data sections. Function descriptors are
159 * stored in the data section.
161 char *db
= (char*)cur
->ldinfo_dataorg
;
162 char *tb
= (char*)cur
->ldinfo_textorg
;
163 char *de
= db
+ cur
->ldinfo_datasize
;
164 char *te
= tb
+ cur
->ldinfo_textsize
;
165 /* Just casting for comparisons. */
169 * Find the symbol's name and base address. To make it
170 * easier, we use the traceback in the text section.
171 * See the function's comments above as to why.
172 * (Perhaps we could deref if a descriptor though...)
174 if (cs
>= tb
&& cs
<= te
) {
175 _g_sym_from_tb(&i
->dli_saddr
, &i
->dli_sname
, s
);
178 if ((cs
>= db
&& cs
<= de
) || (cs
>= tb
&& cs
<= te
)) {
179 /* Look for file name and base address. */
180 i
->dli_fbase
= tb
; /* Includes XCOFF header */
181 /* library filename + ( + member + ) + NUL */
182 char *libname
= (char*)g_alloca (AIX_PRINTED_LIB_LEN
);
183 char *file_part
= cur
->ldinfo_filename
;
184 char *member_part
= file_part
+ strlen(file_part
) + 1;
186 * This can't be a const char*, because it exists from
187 * a stack allocated buffer. Also append the member.
189 * XXX: See if we can't frob usla's memory ranges for
190 * const strings; but is quite difficult.
192 if (member_part
[0] == '\0') {
193 /* Not an archive, just copy the file name. */
194 g_strlcpy(libname
, file_part
, AIX_PRINTED_LIB_LEN
);
196 /* It's an archive with member. */
197 sprintf(libname
, "%s(%s)", file_part
, member_part
);
199 i
->dli_fname
= libname
;
202 } else if (cur
->ldinfo_next
== 0) {
206 /* Try the next image in memory. */
207 cur
= (struct ld_info
*)((char*)cur
+ cur
->ldinfo_next
);
213 g_module_address (void *addr
, char *file_name
, size_t file_name_len
,
214 void **file_base
, char *sym_name
, size_t sym_name_len
,
218 int ret
= _g_dladdr(addr
, &dli
);
219 /* This zero-on-failure is unlike other Unix APIs. */
222 if (file_name
!= NULL
&& file_name_len
>= 1) {
223 if (dli
.dli_fname
!= NULL
)
224 g_strlcpy(file_name
, dli
.dli_fname
, file_name_len
);
226 file_name
[0] = '\0';
228 if (file_base
!= NULL
)
229 *file_base
= dli
.dli_fbase
;
230 if (sym_name
!= NULL
&& sym_name_len
>= 1) {
231 if (dli
.dli_sname
!= NULL
)
232 g_strlcpy(sym_name
, dli
.dli_sname
, sym_name_len
);
236 if (sym_addr
!= NULL
)
237 *sym_addr
= dli
.dli_saddr
;
243 #define MONO_EMPTY_SOURCE_FILE(x) extern const char mono_quash_linker_empty_file_warning_ ## x; \
244 const char mono_quash_linker_empty_file_warning_ ## x = 0;
246 MONO_EMPTY_SOURCE_FILE (gmodule_aix
);