1 /*
   2  * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2012, 2019 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.
  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 
  84 public:
  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 
 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 
 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 
 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 
 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).
 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 
 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.
 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 " SIZE_FORMAT ".", 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 " SIZE_FORMAT ", %p " SIZE_FORMAT ", %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.
 315 bool LoadedLibraries::reload() {
 316   MiscUtils::AutoCritSect lck(&g_cs);
 317   return reload_table();
 318 }
 319 
 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 
 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 
 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