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