1 /*
2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3  * Copyright (c) 2012, 2015 SAP SE. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.
9  *
10  * This code is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * version 2 for more details (a copy is included in the LICENSE file that
14  * accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License version
17  * 2 along with this work; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21  * or visit www.oracle.com if you need additional information or have any
22  * questions.
23  *
24  */
25 
26 
27 // Implementation of LoadedLibraries and friends
28 
29 // Ultimately this just uses loadquery()
30 // See:
31 // http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp
32 //      ?topic=/com.ibm.aix.basetechref/doc/basetrf1/loadquery.htm
33 
34 #include "loadlib_aix.hpp"
35 #include "misc_aix.hpp"
36 #include "porting_aix.hpp"
37 #include "utilities/debug.hpp"
38 #include "utilities/ostream.hpp"
39 
40 // For loadquery()
41 #include <sys/ldr.h>
42 
43 // Use raw malloc instead of os::malloc - this code gets used for error reporting.
44 
45 // A class to "intern" eternal strings.
46 // TODO: similar coding exists in AIX version of dladdr and potentially elsewhere: consolidate!
47 class StringList {
48 
49   char** _list;
50   int _cap;
51   int _num;
52 
53   // Enlarge list. If oom, leave old list intact and return false.
enlarge()54   bool enlarge() {
55     int cap2 = _cap + 64;
56     char** l2 = (char**) ::realloc(_list, sizeof(char*) * cap2);
57     if (!l2) {
58       return false;
59     }
60     _list = l2;
61     _cap = cap2;
62     return true;
63   }
64 
65   // Append string to end of list.
66   // Returns NULL if oom.
append(const char * s)67   char* append(const char* s) {
68     if (_cap == _num) {
69       if (!enlarge()) {
70         return NULL;
71       }
72     }
73     assert0(_cap > _num);
74     char* s2 = ::strdup(s);
75     if (!s2) {
76       return NULL;
77     }
78     _list[_num] = s2;
79     trcVerbose("StringDir: added %s at pos %d", s2, _num);
80     _num ++;
81     return s2;
82   }
83 
84 public:
85 
StringList()86   StringList()
87     : _list(NULL)
88     , _cap(0)
89     , _num(0)
90   {}
91 
92   // String is copied into the list; pointer to copy is returned.
93   // Returns NULL if oom.
add(const char * s)94   char* add (const char* s) {
95     for (int i = 0; i < _num; i++) {
96       if (strcmp(_list[i], s) == 0) {
97         return _list[i];
98       }
99     }
100     return append(s);
101   }
102 
103 };
104 
105 static StringList g_stringlist;
106 
107 //////////////////////
108 
109 // Entries are kept in a linked list ordered by text address. Entries are not
110 // eternal - this list is rebuilt on every reload.
111 // Note that we do not hand out those entries, but copies of them.
112 
113 struct entry_t {
114   entry_t* next;
115   loaded_module_t info;
116 };
117 
print_entry(const entry_t * e,outputStream * os)118 static void print_entry(const entry_t* e, outputStream* os) {
119   const loaded_module_t* const lm = &(e->info);
120   os->print(" %c text: " INTPTR_FORMAT " - " INTPTR_FORMAT
121             ", data: " INTPTR_FORMAT " - " INTPTR_FORMAT " "
122             "%s",
123       (lm->is_in_vm ? '*' : ' '),
124       lm->text, (uintptr_t)lm->text + lm->text_len,
125       lm->data, (uintptr_t)lm->data + lm->data_len,
126       lm->path);
127   if (lm->member) {
128     os->print("(%s)", lm->member);
129   }
130 }
131 
132 static entry_t* g_first = NULL;
133 
find_entry_for_text_address(const void * p)134 static entry_t* find_entry_for_text_address(const void* p) {
135   for (entry_t* e = g_first; e; e = e->next) {
136     if ((uintptr_t)p >= (uintptr_t)e->info.text &&
137         (uintptr_t)p < ((uintptr_t)e->info.text + e->info.text_len)) {
138       return e;
139     }
140   }
141   return NULL;
142 }
143 
find_entry_for_data_address(const void * p)144 static entry_t* find_entry_for_data_address(const void* p) {
145   for (entry_t* e = g_first; e; e = e->next) {
146     if ((uintptr_t)p >= (uintptr_t)e->info.data &&
147         (uintptr_t)p < ((uintptr_t)e->info.data + e->info.data_len)) {
148       return e;
149     }
150   }
151   return NULL;
152 }
153 
154 // Adds a new entry to the list (ordered by text address ascending).
add_entry_to_list(entry_t * e,entry_t ** start)155 static void add_entry_to_list(entry_t* e, entry_t** start) {
156   entry_t* last = NULL;
157   entry_t* e2 = *start;
158   while (e2 && e2->info.text < e->info.text) {
159     last = e2;
160     e2 = e2->next;
161   }
162   if (last) {
163     last->next = e;
164   } else {
165     *start = e;
166   }
167   e->next = e2;
168 }
169 
free_entry_list(entry_t ** start)170 static void free_entry_list(entry_t** start) {
171   entry_t* e = *start;
172   while (e) {
173     entry_t* const e2 = e->next;
174     ::free(e);
175     e = e2;
176   }
177   *start = NULL;
178 }
179 
180 
181 // Rebuild the internal module table. If an error occurs, old table remains
182 // unchanged.
reload_table()183 static bool reload_table() {
184 
185   bool rc = false;
186 
187   trcVerbose("reload module table...");
188 
189   entry_t* new_list = NULL;
190   const struct ld_info* ldi = NULL;
191 
192   // Call loadquery(L_GETINFO..) to get a list of all loaded Dlls from AIX. loadquery
193   // requires a large enough buffer.
194   uint8_t* buffer = NULL;
195   size_t buflen = 1024;
196   for (;;) {
197     buffer = (uint8_t*) ::realloc(buffer, buflen);
198     if (loadquery(L_GETINFO, buffer, buflen) == -1) {
199       if (errno == ENOMEM) {
200         buflen *= 2;
201       } else {
202         trcVerbose("loadquery failed (%d)", errno);
203         goto cleanup;
204       }
205     } else {
206       break;
207     }
208   }
209 
210   trcVerbose("loadquery buffer size is %llu.", buflen);
211 
212   // Iterate over the loadquery result. For details see sys/ldr.h on AIX.
213   ldi = (struct ld_info*) buffer;
214 
215   for (;;) {
216 
217     entry_t* e = (entry_t*) ::malloc(sizeof(entry_t));
218     if (!e) {
219       trcVerbose("OOM.");
220       goto cleanup;
221     }
222 
223     memset(e, 0, sizeof(entry_t));
224 
225     e->info.text = ldi->ldinfo_textorg;
226     e->info.text_len = ldi->ldinfo_textsize;
227     e->info.data = ldi->ldinfo_dataorg;
228     e->info.data_len = ldi->ldinfo_datasize;
229 
230     e->info.path = g_stringlist.add(ldi->ldinfo_filename);
231     if (!e->info.path) {
232       trcVerbose("OOM.");
233       goto cleanup;
234     }
235 
236     // Extract short name
237     {
238       const char* p = strrchr(e->info.path, '/');
239       if (p) {
240         p ++;
241         e->info.shortname = p;
242       } else {
243         e->info.shortname = e->info.path;
244       }
245     }
246 
247     // Do we have a member name as well (see ldr.h)?
248     const char* p_mbr_name =
249       ldi->ldinfo_filename + strlen(ldi->ldinfo_filename) + 1;
250     if (*p_mbr_name) {
251       e->info.member = g_stringlist.add(p_mbr_name);
252       if (!e->info.member) {
253         trcVerbose("OOM.");
254         goto cleanup;
255       }
256     } else {
257       e->info.member = NULL;
258     }
259 
260     if (strcmp(e->info.shortname, "libjvm.so") == 0) {
261       // Note that this, theoretically, is fuzzy. We may accidentally contain
262       // more than one libjvm.so. But that is improbable, so lets go with this
263       // solution.
264       e->info.is_in_vm = true;
265     }
266 
267     trcVerbose("entry: %p %llu, %p %llu, %s %s %s, %d",
268       e->info.text, e->info.text_len,
269       e->info.data, e->info.data_len,
270       e->info.path, e->info.shortname,
271       (e->info.member ? e->info.member : "NULL"),
272       e->info.is_in_vm
273     );
274 
275     // Add to list.
276     add_entry_to_list(e, &new_list);
277 
278     // Next entry...
279     if (ldi->ldinfo_next) {
280       ldi = (struct ld_info*)(((char*)ldi) + ldi->ldinfo_next);
281     } else {
282       break;
283     }
284   }
285 
286   // We are done. All is well. Free old list and swap to new one.
287   if (g_first) {
288     free_entry_list(&g_first);
289   }
290   g_first = new_list;
291   new_list = NULL;
292 
293   rc = true;
294 
295 cleanup:
296 
297   if (new_list) {
298     free_entry_list(&new_list);
299   }
300 
301   ::free(buffer);
302 
303   return rc;
304 
305 } // end LoadedLibraries::reload()
306 
307 
308 ///////////////////////////////////////////////////////////////////////////////
309 // Externals
310 
311 static MiscUtils::CritSect g_cs;
312 
313 // Rebuild the internal module table. If an error occurs, old table remains
314 // unchanged.
reload()315 bool LoadedLibraries::reload() {
316   MiscUtils::AutoCritSect lck(&g_cs);
317   return reload_table();
318 }
319 
print(outputStream * os)320 void LoadedLibraries::print(outputStream* os) {
321   MiscUtils::AutoCritSect lck(&g_cs);
322   if (!g_first) {
323     reload_table();
324   }
325   for (entry_t* e = g_first; e; e = e->next) {
326     print_entry(e, os);
327     os->cr();
328   }
329 }
330 
find_for_text_address(const void * p,loaded_module_t * info)331 bool LoadedLibraries::find_for_text_address(const void* p,
332                                             loaded_module_t* info) {
333   MiscUtils::AutoCritSect lck(&g_cs);
334   if (!g_first) {
335     reload_table();
336   }
337   const entry_t* const e = find_entry_for_text_address(p);
338   if (e) {
339     if (info) {
340       *info = e->info;
341     }
342     return true;
343   }
344   return false;
345 }
346 
347 
find_for_data_address(const void * p,loaded_module_t * info)348 bool LoadedLibraries::find_for_data_address (
349   const void* p,
350   loaded_module_t* info // optional. can be NULL:
351 ) {
352   MiscUtils::AutoCritSect lck(&g_cs);
353   if (!g_first) {
354     reload_table();
355   }
356   const entry_t* const e = find_entry_for_data_address(p);
357   if (e) {
358     if (info) {
359       *info = e->info;
360     }
361     return true;
362   }
363   return false;
364 }
365 
366