loadlib_aix.cpp revision 13159:93edfbc19d0c
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!
47class StringList {
48
49  char** _list;
50  int _cap;
51  int _num;
52
53  // Enlarge list. If oom, leave old list intact and return false.
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.
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
84public:
85
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.
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
105static 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
113struct entry_t {
114  entry_t* next;
115  loaded_module_t info;
116};
117
118static 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
132static entry_t* g_first = NULL;
133
134static 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
144static 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).
155static 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
170static 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.
183static 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
295cleanup:
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
311static MiscUtils::CritSect g_cs;
312
313// Rebuild the internal module table. If an error occurs, old table remains
314// unchanged.
315bool LoadedLibraries::reload() {
316  MiscUtils::AutoCritSect lck(&g_cs);
317  return reload_table();
318}
319
320void 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
331bool 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
348bool 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