hotspot/src/os/aix/vm/loadlib_aix.cpp
author kbarrett
Tue, 13 Jun 2017 11:10:33 -0400
changeset 46535 fc2445cc0e3d
parent 35594 cc13089c6327
permissions -rw-r--r--
8086005: Define __STDC_xxx_MACROS config macros globally via build system Summary: Moved macro definitions to hotspot build configuration Reviewed-by: erikj, eosterlund

/*
 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2012, 2015 SAP SE. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */


// Implementation of LoadedLibraries and friends

// Ultimately this just uses loadquery()
// See:
// http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp
//      ?topic=/com.ibm.aix.basetechref/doc/basetrf1/loadquery.htm

#include "loadlib_aix.hpp"
#include "misc_aix.hpp"
#include "porting_aix.hpp"
#include "utilities/debug.hpp"
#include "utilities/ostream.hpp"

// For loadquery()
#include <sys/ldr.h>

// Use raw malloc instead of os::malloc - this code gets used for error reporting.

// A class to "intern" eternal strings.
// TODO: similar coding exists in AIX version of dladdr and potentially elsewhere: consolidate!
class StringList {

  char** _list;
  int _cap;
  int _num;

  // Enlarge list. If oom, leave old list intact and return false.
  bool enlarge() {
    int cap2 = _cap + 64;
    char** l2 = (char**) ::realloc(_list, sizeof(char*) * cap2);
    if (!l2) {
      return false;
    }
    _list = l2;
    _cap = cap2;
    return true;
  }

  // Append string to end of list.
  // Returns NULL if oom.
  char* append(const char* s) {
    if (_cap == _num) {
      if (!enlarge()) {
        return NULL;
      }
    }
    assert0(_cap > _num);
    char* s2 = ::strdup(s);
    if (!s2) {
      return NULL;
    }
    _list[_num] = s2;
    trcVerbose("StringDir: added %s at pos %d", s2, _num);
    _num ++;
    return s2;
  }

public:

  StringList()
    : _list(NULL)
    , _cap(0)
    , _num(0)
  {}

  // String is copied into the list; pointer to copy is returned.
  // Returns NULL if oom.
  char* add (const char* s) {
    for (int i = 0; i < _num; i++) {
      if (strcmp(_list[i], s) == 0) {
        return _list[i];
      }
    }
    return append(s);
  }

};

static StringList g_stringlist;

//////////////////////

// Entries are kept in a linked list ordered by text address. Entries are not
// eternal - this list is rebuilt on every reload.
// Note that we do not hand out those entries, but copies of them.

struct entry_t {
  entry_t* next;
  loaded_module_t info;
};

static void print_entry(const entry_t* e, outputStream* os) {
  const loaded_module_t* const lm = &(e->info);
  os->print(" %c text: " INTPTR_FORMAT " - " INTPTR_FORMAT
            ", data: " INTPTR_FORMAT " - " INTPTR_FORMAT " "
            "%s",
      (lm->is_in_vm ? '*' : ' '),
      lm->text, (uintptr_t)lm->text + lm->text_len,
      lm->data, (uintptr_t)lm->data + lm->data_len,
      lm->path);
  if (lm->member) {
    os->print("(%s)", lm->member);
  }
}

static entry_t* g_first = NULL;

static entry_t* find_entry_for_text_address(const void* p) {
  for (entry_t* e = g_first; e; e = e->next) {
    if ((uintptr_t)p >= (uintptr_t)e->info.text &&
        (uintptr_t)p < ((uintptr_t)e->info.text + e->info.text_len)) {
      return e;
    }
  }
  return NULL;
}

static entry_t* find_entry_for_data_address(const void* p) {
  for (entry_t* e = g_first; e; e = e->next) {
    if ((uintptr_t)p >= (uintptr_t)e->info.data &&
        (uintptr_t)p < ((uintptr_t)e->info.data + e->info.data_len)) {
      return e;
    }
  }
  return NULL;
}

// Adds a new entry to the list (ordered by text address ascending).
static void add_entry_to_list(entry_t* e, entry_t** start) {
  entry_t* last = NULL;
  entry_t* e2 = *start;
  while (e2 && e2->info.text < e->info.text) {
    last = e2;
    e2 = e2->next;
  }
  if (last) {
    last->next = e;
  } else {
    *start = e;
  }
  e->next = e2;
}

static void free_entry_list(entry_t** start) {
  entry_t* e = *start;
  while (e) {
    entry_t* const e2 = e->next;
    ::free(e);
    e = e2;
  }
  *start = NULL;
}


// Rebuild the internal module table. If an error occurs, old table remains
// unchanged.
static bool reload_table() {

  bool rc = false;

  trcVerbose("reload module table...");

  entry_t* new_list = NULL;
  const struct ld_info* ldi = NULL;

  // Call loadquery(L_GETINFO..) to get a list of all loaded Dlls from AIX. loadquery
  // requires a large enough buffer.
  uint8_t* buffer = NULL;
  size_t buflen = 1024;
  for (;;) {
    buffer = (uint8_t*) ::realloc(buffer, buflen);
    if (loadquery(L_GETINFO, buffer, buflen) == -1) {
      if (errno == ENOMEM) {
        buflen *= 2;
      } else {
        trcVerbose("loadquery failed (%d)", errno);
        goto cleanup;
      }
    } else {
      break;
    }
  }

  trcVerbose("loadquery buffer size is %llu.", buflen);

  // Iterate over the loadquery result. For details see sys/ldr.h on AIX.
  ldi = (struct ld_info*) buffer;

  for (;;) {

    entry_t* e = (entry_t*) ::malloc(sizeof(entry_t));
    if (!e) {
      trcVerbose("OOM.");
      goto cleanup;
    }

    memset(e, 0, sizeof(entry_t));

    e->info.text = ldi->ldinfo_textorg;
    e->info.text_len = ldi->ldinfo_textsize;
    e->info.data = ldi->ldinfo_dataorg;
    e->info.data_len = ldi->ldinfo_datasize;

    e->info.path = g_stringlist.add(ldi->ldinfo_filename);
    if (!e->info.path) {
      trcVerbose("OOM.");
      goto cleanup;
    }

    // Extract short name
    {
      const char* p = strrchr(e->info.path, '/');
      if (p) {
        p ++;
        e->info.shortname = p;
      } else {
        e->info.shortname = e->info.path;
      }
    }

    // Do we have a member name as well (see ldr.h)?
    const char* p_mbr_name =
      ldi->ldinfo_filename + strlen(ldi->ldinfo_filename) + 1;
    if (*p_mbr_name) {
      e->info.member = g_stringlist.add(p_mbr_name);
      if (!e->info.member) {
        trcVerbose("OOM.");
        goto cleanup;
      }
    } else {
      e->info.member = NULL;
    }

    if (strcmp(e->info.shortname, "libjvm.so") == 0) {
      // Note that this, theoretically, is fuzzy. We may accidentally contain
      // more than one libjvm.so. But that is improbable, so lets go with this
      // solution.
      e->info.is_in_vm = true;
    }

    trcVerbose("entry: %p %llu, %p %llu, %s %s %s, %d",
      e->info.text, e->info.text_len,
      e->info.data, e->info.data_len,
      e->info.path, e->info.shortname,
      (e->info.member ? e->info.member : "NULL"),
      e->info.is_in_vm
    );

    // Add to list.
    add_entry_to_list(e, &new_list);

    // Next entry...
    if (ldi->ldinfo_next) {
      ldi = (struct ld_info*)(((char*)ldi) + ldi->ldinfo_next);
    } else {
      break;
    }
  }

  // We are done. All is well. Free old list and swap to new one.
  if (g_first) {
    free_entry_list(&g_first);
  }
  g_first = new_list;
  new_list = NULL;

  rc = true;

cleanup:

  if (new_list) {
    free_entry_list(&new_list);
  }

  ::free(buffer);

  return rc;

} // end LoadedLibraries::reload()


///////////////////////////////////////////////////////////////////////////////
// Externals

static MiscUtils::CritSect g_cs;

// Rebuild the internal module table. If an error occurs, old table remains
// unchanged.
bool LoadedLibraries::reload() {
  MiscUtils::AutoCritSect lck(&g_cs);
  return reload_table();
}

void LoadedLibraries::print(outputStream* os) {
  MiscUtils::AutoCritSect lck(&g_cs);
  if (!g_first) {
    reload_table();
  }
  for (entry_t* e = g_first; e; e = e->next) {
    print_entry(e, os);
    os->cr();
  }
}

bool LoadedLibraries::find_for_text_address(const void* p,
                                            loaded_module_t* info) {
  MiscUtils::AutoCritSect lck(&g_cs);
  if (!g_first) {
    reload_table();
  }
  const entry_t* const e = find_entry_for_text_address(p);
  if (e) {
    if (info) {
      *info = e->info;
    }
    return true;
  }
  return false;
}


bool LoadedLibraries::find_for_data_address (
  const void* p,
  loaded_module_t* info // optional. can be NULL:
) {
  MiscUtils::AutoCritSect lck(&g_cs);
  if (!g_first) {
    reload_table();
  }
  const entry_t* const e = find_entry_for_data_address(p);
  if (e) {
    if (info) {
      *info = e->info;
    }
    return true;
  }
  return false;
}