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 #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!
  50 class 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 
  87 public:
  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 
 108 static 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 
 116 struct entry_t {
 117   entry_t* next;
 118   loaded_module_t info;
 119 };
 120 
 121 static 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 
 135 static entry_t* g_first = NULL;
 136 
 137 static 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 
 147 static 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).
 158 static 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 
 173 static 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.
 186 static 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 
 298 cleanup:
 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 
 314 static MiscUtils::CritSect g_cs;
 315 
 316 // Rebuild the internal module table. If an error occurs, old table remains
 317 // unchanged.
 318 bool LoadedLibraries::reload() {
 319   MiscUtils::AutoCritSect lck(&g_cs);
 320   return reload_table();
 321 }
 322 
 323 void 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 
 334 bool 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 
 351 bool 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