dladdr shim for gmodule; try to enable crash reporter on AIX (#15808)
[mono-project.git] / mono / eglib / gmodule-aix.c
blob7875967674dd17e19704231e46b61351cdd65580
1 /*
2 * gmodule.c: dl* functions, glib style
4 * Author:
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)
36 #include <config.h>
38 #include <glib.h>
39 #include <gmodule.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <dlfcn.h>
44 #include <sys/errno.h>
45 #include <stdlib.h>
47 /* AIX specific headers for loadquery and traceback structure */
48 #include <sys/ldr.h>
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 {
61 char* dli_fname;
62 void* dli_fbase;
63 const char* dli_sname;
64 void* dli_saddr;
65 } _g_Dl_info;
67 /**
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...
82 static void
83 _g_sym_from_tb(void **sbase, const char **sname, void *where) {
84 unsigned int *s = (unsigned int*)where;
85 while (*s) {
86 /* look for zero word (invalid op) that begins epilogue */
87 s++;
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);
97 if (tb->has_tboff) {
98 /* tb_offset */
99 void *start = (char*)s - *((unsigned int*)ext);
100 ext += sizeof (unsigned int);
101 *sbase = (void*)start;
102 } else {
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 */
110 if (tb->int_hndl) {
111 ext += sizeof(int);
113 if (tb->has_ctl) {
114 /* array */
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);
125 *sname = ext;
126 } else {
127 *sname = NULL;
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.
139 static int
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);
146 i->dli_fbase = NULL;
147 i->dli_fname = NULL;
148 i->dli_saddr = NULL;
149 i->dli_sname = NULL;
150 int r = loadquery (L_GETINFO, buf, 10000);
151 if (r == -1) {
152 return 0;
154 /* The loader info structures are also a linked list. */
155 struct ld_info *cur = (struct ld_info*) buf;
156 while (1) {
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. */
166 char *cs = (char*)s;
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);
195 } else {
196 /* It's an archive with member. */
197 sprintf(libname, "%s(%s)", file_part, member_part);
199 i->dli_fname = libname;
201 return 1;
202 } else if (cur->ldinfo_next == 0) {
203 /* Nothing. */
204 return 0;
205 } else {
206 /* Try the next image in memory. */
207 cur = (struct ld_info*)((char*)cur + cur->ldinfo_next);
212 gboolean
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,
215 void **sym_addr)
217 _g_Dl_info dli;
218 int ret = _g_dladdr(addr, &dli);
219 /* This zero-on-failure is unlike other Unix APIs. */
220 if (ret == 0)
221 return FALSE;
222 if (file_name != NULL && file_name_len >= 1)
223 g_strlcpy(file_name, dli.dli_fname, file_name_len);
224 if (file_base != NULL)
225 *file_base = dli.dli_fbase;
226 if (sym_name != NULL && sym_name_len >= 1)
227 g_strlcpy(sym_name, dli.dli_sname, sym_name_len);
228 if (sym_addr != NULL)
229 *sym_addr = dli.dli_saddr;
230 return TRUE;
232 #endif