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