blob: 4366497064e9bbe79ef6fdf195d7eb04851d562e [file] [log] [blame]
/* *******************************************************************************
* Copyright (c) 2011-2014 Google, Inc. All rights reserved.
* Copyright (c) 2010 Massachusetts Institute of Technology All rights reserved.
* Copyright (c) 2009 Derek Bruening All rights reserved.
* *******************************************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/*
* loader_shared.c: os independent code of DR's custom private library loader
*
* original case: i#157
*/
#include "globals.h"
#include "module_shared.h"
#ifdef CLIENT_INTERFACE
# include "instrument.h" /* for instrument_client_lib_unloaded */
#endif
#include <string.h>
/* ok to be in .data w/ no sentinel head node b/c never empties out
* .ntdll always there for Windows, so no need to unprot.
* XXX: Does it hold for Linux?
* It seems no library is must in Linux, even the loader.
* Maybe the linux-gate or we just create a fake one?
*/
static privmod_t *modlist;
/* Recursive library load could happen:
* Linux: when load dependent library
* Windows: redirect_* can be invoked from private libray
* entry points.
*/
DECLARE_CXTSWPROT_VAR(recursive_lock_t privload_lock,
INIT_RECURSIVE_LOCK(privload_lock));
/* Protected by privload_lock */
#ifdef DEBUG
DECLARE_NEVERPROT_VAR(static uint privload_recurse_cnt, 0);
#endif
/* These are only written during init so ok to be in .data */
static privmod_t privmod_static[PRIVMOD_STATIC_NUM];
/* this marks end of privmod_static[] */
uint privmod_static_idx;
/* We store client paths here to use for locating libraries later
* Unfortunately we can't use dynamic storage, and the paths are clobbered
* immediately by instrument_load_client_libs, so we have max space here.
*/
char search_paths[SEARCH_PATHS_NUM][MAXIMUM_PATH];
/* this marks end of search_paths[] */
uint search_paths_idx;
/* Used for in_private_library() */
vm_area_vector_t *modlist_areas;
/* forward decls */
static bool
privload_load_finalize(privmod_t *privmod);
static bool
privload_has_thread_entry(void);
static bool
privload_modlist_initialized(void);
/***************************************************************************/
void
loader_init(void)
{
uint i;
privmod_t *mod;
acquire_recursive_lock(&privload_lock);
VMVECTOR_ALLOC_VECTOR(modlist_areas, GLOBAL_DCONTEXT,
VECTOR_SHARED | VECTOR_NEVER_MERGE
/* protected by privload_lock */
| VECTOR_NO_LOCK,
modlist_areas);
/* os specific loader initialization prologue before finalize the load */
os_loader_init_prologue();
/* Process client libs we loaded early but did not finalize */
for (i = 0; i < privmod_static_idx; i++) {
/* Transfer to real list so we can do normal processing */
char name_copy[MAXIMUM_PATH];
mod = privload_insert(NULL,
privmod_static[i].base,
privmod_static[i].size,
privmod_static[i].name,
privmod_static[i].path);
LOG(GLOBAL, LOG_LOADER, 1, "%s: processing imports for %s\n",
__FUNCTION__, mod->name);
/* save a copy for error msg, b/c mod will be unloaded (i#643) */
snprintf(name_copy, BUFFER_SIZE_ELEMENTS(name_copy), "%s", mod->name);
NULL_TERMINATE_BUFFER(name_copy);
if (!privload_load_finalize(mod)) {
mod = NULL; /* it's been unloaded! */
#ifdef CLIENT_INTERFACE
SYSLOG(SYSLOG_ERROR, CLIENT_LIBRARY_UNLOADABLE, 5,
get_application_name(), get_application_pid(), name_copy,
"\n\tUnable to locate imports of client library");
#endif
os_terminate(NULL, TERMINATE_PROCESS);
ASSERT_NOT_REACHED();
}
}
/* os specific loader initialization epilogue after finalize the load */
os_loader_init_epilogue();
/* FIXME i#338: call loader_thread_init here once get
* loader_init called after dynamo_thread_init but in a way that
* works with Windows
*/
release_recursive_lock(&privload_lock);
}
void
loader_exit(void)
{
/* We must unload for detach so can't leave them loaded */
acquire_recursive_lock(&privload_lock);
/* The list is kept in reverse-dependent order so we can unload from the
* front without breaking dependencies.
*/
while (modlist != NULL)
privload_unload(modlist);
/* os related loader finalization */
os_loader_exit();
vmvector_delete_vector(GLOBAL_DCONTEXT, modlist_areas);
release_recursive_lock(&privload_lock);
DELETE_RECURSIVE_LOCK(privload_lock);
}
void
loader_thread_init(dcontext_t *dcontext)
{
privmod_t *mod;
if (modlist == NULL) {
#ifdef WINDOWS
/* FIXME i#338: once restore order this will become nop */
/* os specific thread initilization prologue for loader with no lock */
os_loader_thread_init_prologue(dcontext);
/* os specific thread initilization epilogue for loader with no lock */
os_loader_thread_init_epilogue(dcontext);
#endif /* WINDOWS */
} else {
/* os specific thread initilization prologue for loader with no lock */
os_loader_thread_init_prologue(dcontext);
if (privload_has_thread_entry()) {
/* We rely on lock isolation to prevent deadlock while we're here
* holding privload_lock and the priv lib
* DllMain may acquire the same lock that another thread acquired
* in its app code before requesting a synchall (flush, exit).
* FIXME i#875: we do not have ntdll!RtlpFlsLock isolated.
* Living w/ it for now. It should be unlikely for the app to
* hold RtlpFlsLock and then acquire privload_lock: privload_lock
* is used for import redirection but those don't apply within
* ntdll.
*/
ASSERT_OWN_NO_LOCKS();
acquire_recursive_lock(&privload_lock);
/* Walk forward and call independent libs last.
* We do notify priv libs of client threads.
*/
for (mod = modlist; mod != NULL; mod = mod->next) {
if (!mod->externally_loaded)
privload_call_entry(mod, DLL_THREAD_INIT);
}
release_recursive_lock(&privload_lock);
}
/* os specific thread initilization epilogue for loader with no lock */
os_loader_thread_init_epilogue(dcontext);
}
}
void
loader_thread_exit(dcontext_t *dcontext)
{
privmod_t *mod;
/* assuming context swap have happened when entered DR */
if (privload_has_thread_entry() &&
/* Only call if we're cleaning up the currently executing thread, as
* that's what the entry routine is going to do! Calling on other
* threads results in problems like double frees (i#969). Exiting
* another thread should only happen on process exit or forced thread
* termination. The former can technically continue (app could call
* NtTerminateProcess(0) but then keep going) but we have never seen
* that; and the latter doesn't do full native cleanups anyway. Thus
* we're not worried about leaks from not calling DLL_THREAD_EXIT.
* (We can't check get_thread_private_dcontext() b/c it's already cleared.)
*/
dcontext->owning_thread == get_thread_id()) {
acquire_recursive_lock(&privload_lock);
/* Walk forward and call independent libs last */
for (mod = modlist; mod != NULL; mod = mod->next) {
if (!mod->externally_loaded)
privload_call_entry(mod, DLL_THREAD_EXIT);
}
release_recursive_lock(&privload_lock);
}
/* os specific thread exit for loader, holding no lock */
os_loader_thread_exit(dcontext);
}
/* Given a path-less name, locates and loads a private library for DR's client.
* Will also accept a full path.
*/
app_pc
locate_and_load_private_library(const char *name, bool reachable)
{
DODEBUG(privload_recurse_cnt = 0;);
return privload_load_private_library(name, reachable);
}
/* Load private library for DR's client. Must be passed a full path. */
app_pc
load_private_library(const char *filename, bool reachable)
{
app_pc res = NULL;
privmod_t *privmod;
/* Simpler to lock up front than to unmap on race. All helper routines
* assume the lock is held.
*/
acquire_recursive_lock(&privload_lock);
privmod = privload_lookup(filename);
/* XXX: If the private lib has been loaded, shall we increase the counter
* or report error?
*/
if (privmod == NULL) {
DODEBUG(privload_recurse_cnt = 0;);
privmod = privload_load(filename, NULL, reachable);
}
if (privmod != NULL)
res = privmod->base;
release_recursive_lock(&privload_lock);
return res;
}
bool
unload_private_library(app_pc modbase)
{
privmod_t *mod;
bool res = false;
acquire_recursive_lock(&privload_lock);
mod = privload_lookup_by_base(modbase);
if (mod != NULL)
res = privload_unload(mod);
release_recursive_lock(&privload_lock);
return res;
}
bool
in_private_library(app_pc pc)
{
return vmvector_overlap(modlist_areas, pc, pc+1);
}
/* Lookup the private loaded library either by basename or by path */
privmod_t *
privload_lookup(const char *name)
{
privmod_t *mod;
bool by_path;
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
if (name == NULL || name[0] == '\0')
return NULL;
by_path = IF_WINDOWS_ELSE(double_strrchr(name, DIRSEP, ALT_DIRSEP),
strrchr(name, DIRSEP)) != NULL;
if (!privload_modlist_initialized()) {
uint i;
for (i = 0; i < privmod_static_idx; i++) {
mod = &privmod_static[i];
if ((by_path && strcasecmp(name, mod->path) == 0) ||
(!by_path && strcasecmp(name, mod->name) == 0))
return mod;
}
} else {
for (mod = modlist; mod != NULL; mod = mod->next) {
if ((by_path && strcasecmp(name, mod->path) == 0) ||
(!by_path && strcasecmp(name, mod->name) == 0))
return mod;
}
}
return NULL;
}
/* Lookup the private loaded library by base */
privmod_t *
privload_lookup_by_base(app_pc modbase)
{
privmod_t *mod;
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
if (!privload_modlist_initialized()) {
uint i;
for (i = 0; i < privmod_static_idx; i++) {
if (privmod_static[i].base == modbase)
return &privmod_static[i];
}
} else {
for (mod = modlist; mod != NULL; mod = mod->next) {
if (modbase == mod->base)
return mod;
}
}
return NULL;
}
/* Lookup the private loaded library by base */
privmod_t *
privload_lookup_by_pc(app_pc pc)
{
privmod_t *mod;
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
if (!privload_modlist_initialized()) {
uint i;
for (i = 0; i < privmod_static_idx; i++) {
if (pc >= privmod_static[i].base &&
pc < privmod_static[i].base + privmod_static[i].size)
return &privmod_static[i];
}
} else {
for (mod = modlist; mod != NULL; mod = mod->next) {
if (pc >= mod->base && pc < mod->base + mod->size)
return mod;
}
}
return NULL;
}
/* Insert privmod after *after
* name is assumed to be in immutable persistent storage.
* a copy of path is made.
*/
privmod_t *
privload_insert(privmod_t *after, app_pc base, size_t size, const char *name,
const char *path)
{
privmod_t *mod;
/* We load client libs before heap is initialized so we use a
* static array of initial privmod_t structs until we can fully
* load and create proper list entries.
*/
if (privload_modlist_initialized())
mod = HEAP_TYPE_ALLOC(GLOBAL_DCONTEXT, privmod_t, ACCT_OTHER, PROTECTED);
else {
/* temporarily use array */
if (privmod_static_idx >= PRIVMOD_STATIC_NUM) {
ASSERT_NOT_REACHED();
return NULL;
}
mod = &privmod_static[privmod_static_idx];
++privmod_static_idx;
++search_paths_idx;
}
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
mod->base = base;
mod->size = size;
mod->name = name;
strncpy(mod->path, path, BUFFER_SIZE_ELEMENTS(mod->path));
mod->os_privmod_data = NULL; /* filled in later */
NULL_TERMINATE_BUFFER(mod->path);
/* i#489 DT_SONAME is optional and name passed in could be NULL.
* If so, we get libname from path instead.
*/
if (IF_UNIX_ELSE(mod->name == NULL, false)) {
mod->name = double_strrchr(mod->path, DIRSEP, ALT_DIRSEP);
if (mod->name == NULL)
mod->name = mod->path;
}
mod->ref_count = 1;
mod->externally_loaded = false;
#ifdef CLIENT_INTERFACE
mod->is_client = false; /* up to caller to set later */
#endif
/* do not add non-heap struct to list: in init() we'll move array to list */
if (privload_modlist_initialized()) {
if (after == NULL) {
bool prot = DATASEC_PROTECTED(DATASEC_RARELY_PROT);
mod->next = modlist;
mod->prev = NULL;
if (prot)
SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
if (modlist != NULL)
modlist->prev = mod;
modlist = mod;
if (prot)
SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);
} else {
/* we insert after dependent libs so we can unload in forward order */
mod->prev = after;
mod->next = after->next;
if (after->next != NULL)
after->next->prev = mod;
after->next = mod;
}
}
return (void *)mod;
}
static bool
privload_search_path_exists(const char *path, size_t len)
{
uint i;
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
for (i = 0; i < search_paths_idx; i++) {
if (IF_UNIX_ELSE(strncmp,strncasecmp)(search_paths[i], path, len) == 0)
return true;
}
return false;
}
/* i#955: we support a <basename>.drpath text file listing search paths.
* XXX i#1078: should we support something like DT_RPATH's $ORIGIN for relative
* entries in this file?
*/
static void
privload_read_drpath_file(const char *libname)
{
char path[MAXIMUM_PATH];
char *end = strrchr(libname, '.');
if (end == NULL)
return;
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
snprintf(path, BUFFER_SIZE_ELEMENTS(path), "%.*s.%s",
end - libname, libname, DR_RPATH_SUFFIX);
NULL_TERMINATE_BUFFER(path);
LOG(GLOBAL, LOG_LOADER, 3, "%s: looking for %s\n", __FUNCTION__, path);
if (os_file_exists(path, false/*!is_dir*/)) {
/* Easiest to parse by mapping. It's a newline-separated list of
* paths. We support carriage returns as well.
*/
file_t f = os_open(path, OS_OPEN_READ);
char *map;
size_t map_size = 0;
uint64 file_size;
if (f != INVALID_FILE &&
os_get_file_size_by_handle(f, &file_size)) {
LOG(GLOBAL, LOG_LOADER, 2, "%s: reading %s\n", __FUNCTION__, path);
map = (char *)
os_map_file(f, &map_size, 0, NULL, MEMPROT_READ, 0);
if (map != NULL && map_size >= file_size) {
const char *s = (char *) map;
const char *nl;
while (s < map + file_size && search_paths_idx < SEARCH_PATHS_NUM) {
for (nl = s; nl < map + file_size && *nl != '\r' && *nl != '\n';
nl++) {
}
if (nl == s)
break;
if (!privload_search_path_exists(s, nl - s)) {
snprintf(search_paths[search_paths_idx],
BUFFER_SIZE_ELEMENTS(search_paths[search_paths_idx]),
"%.*s", nl - s, s);
NULL_TERMINATE_BUFFER(search_paths[search_paths_idx]);
LOG(GLOBAL, LOG_LOADER, 1, "%s: added search dir \"%s\"\n",
__FUNCTION__, search_paths[search_paths_idx]);
search_paths_idx++;
}
s = nl + 1;
while (s < map + file_size && (*s == '\r' || *s == '\n'))
s++;
}
os_unmap_file((byte *)map, map_size);
}
os_close(f);
}
}
}
privmod_t *
privload_load(const char *filename, privmod_t *dependent, bool client)
{
app_pc map;
size_t size;
privmod_t *privmod;
/* i#350: it would be nice to have no-dcontext try/except support:
* then we could wrap the whole load process, like ntdll!Ldr does
*/
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
DOCHECK(1, {
/* we have limited stack but we don't expect deep recursion */
privload_recurse_cnt++;
ASSERT_CURIOSITY(privload_recurse_cnt < 20); /* win7 dbghelp gets to 12 */
});
LOG(GLOBAL, LOG_LOADER, 2, "%s: loading %s\n", __FUNCTION__, filename);
map = privload_map_and_relocate(filename, &size, client);
if (map == NULL) {
LOG(GLOBAL, LOG_LOADER, 1, "%s: failed to map %s\n", __FUNCTION__, filename);
return NULL;
}
/* i#955: support a <basename>.drpath file for search paths */
privload_read_drpath_file(filename);
/* For direct client libs (not dependent libs),
* keep a copy of the lib path for use in searching: we'll strdup in loader_init.
* This needs to come before privload_insert which will inc search_paths_idx.
* There should be very few of these (normally just 1), so we don't call
* privload_search_path_exists() (which would require refactoring when the
* search_paths_idx increment happens).
*/
if (!privload_modlist_initialized()) {
const char *end = double_strrchr(filename, DIRSEP, ALT_DIRSEP);
ASSERT(search_paths_idx < SEARCH_PATHS_NUM);
if (end != NULL &&
end - filename < BUFFER_SIZE_ELEMENTS(search_paths[search_paths_idx])) {
snprintf(search_paths[search_paths_idx], end - filename, "%s",
filename);
NULL_TERMINATE_BUFFER(search_paths[search_paths_idx]);
} else
ASSERT_NOT_REACHED(); /* should never have client lib path so big */
}
/* Add to list before processing imports in case of mutually dependent libs */
/* Since we control when unmapped, we can use orig export name string and
* don't need strdup
*/
/* Add after its dependent to preserve forward-can-unload order */
privmod = privload_insert(dependent, map, size, get_shared_lib_name(map),
filename);
/* If no heap yet, we'll call finalize later in loader_init() */
if (privmod != NULL && privload_modlist_initialized()) {
if (!privload_load_finalize(privmod))
return NULL;
}
#ifdef CLIENT_INTERFACE
if (privmod->is_client)
instrument_client_lib_loaded(privmod->base, privmod->base + privmod->size);
#endif
return privmod;
}
bool
privload_unload(privmod_t *privmod)
{
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
ASSERT(privload_modlist_initialized());
ASSERT(privmod->ref_count > 0);
privmod->ref_count--;
LOG(GLOBAL, LOG_LOADER, 2, "%s: %s refcount => %d\n", __FUNCTION__,
privmod->name, privmod->ref_count);
if (privmod->ref_count == 0) {
LOG(GLOBAL, LOG_LOADER, 1, "%s: unloading %s @ "PFX"\n", __FUNCTION__,
privmod->name, privmod->base);
#ifdef CLIENT_INTERFACE
if (privmod->is_client)
instrument_client_lib_unloaded(privmod->base, privmod->base + privmod->size);
#endif
if (privmod->prev == NULL) {
bool prot = DATASEC_PROTECTED(DATASEC_RARELY_PROT);
if (prot)
SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
modlist = privmod->next;
if (prot)
SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);
} else
privmod->prev->next = privmod->next;
if (privmod->next != NULL)
privmod->next->prev = privmod->prev;
if (!privmod->externally_loaded) {
privload_call_entry(privmod, DLL_PROCESS_EXIT);
/* this routine may modify modlist, but we're done with it */
privload_unload_imports(privmod);
privload_remove_areas(privmod);
/* unmap_file removes from DR areas and calls unmap_file().
* It's ok to call this for client libs: ok to remove what's not there.
*/
privload_unmap_file(privmod);
}
HEAP_TYPE_FREE(GLOBAL_DCONTEXT, privmod, privmod_t, ACCT_OTHER, PROTECTED);
return true;
}
return false;
}
#ifdef X64
# define LIB_SUBDIR "lib64"
#else
# define LIB_SUBDIR "lib32"
#endif
#define EXT_SUBDIR "ext"
#define DRMF_SUBDIR "drmemory/drmf"
static void
privload_add_subdir_path(const char *subdir)
{
const char *path, *mid, *end;
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
/* We support loading from various subdirs of the DR package. We
* locate these by assuming dynamorio.dll is in
* <prefix>/lib{32,64}/{debug,release}/ and searching backward for
* that lib{32,64} part. We assume that "subdir" is followed
* by the same /lib{32,64}/{debug,release}/.
* XXX: this does not work from a build dir: only using exports!
*/
path = get_dynamorio_library_path();
mid = strstr(path, LIB_SUBDIR);
if (mid != NULL &&
search_paths_idx < SEARCH_PATHS_NUM &&
(strlen(path)+strlen(subdir)+1/*sep*/) <
BUFFER_SIZE_ELEMENTS(search_paths[search_paths_idx])) {
char *s = search_paths[search_paths_idx];
snprintf(s, mid - path, "%s", path);
s += (mid - path);
snprintf(s, strlen(subdir)+1/*sep*/, "%s%c", subdir, DIRSEP);
s += strlen(subdir)+1/*sep*/;
end = double_strrchr(path, DIRSEP, ALT_DIRSEP);
if (end != NULL && search_paths_idx < SEARCH_PATHS_NUM) {
snprintf(s, end - mid, "%s", mid);
NULL_TERMINATE_BUFFER(search_paths[search_paths_idx]);
LOG(GLOBAL, LOG_LOADER, 1, "%s: added Extension search dir %s\n",
__FUNCTION__, search_paths[search_paths_idx]);
search_paths_idx++;
}
}
}
void
privload_add_drext_path(void)
{
/* We support loading from the Extensions dir:
* <prefix>/ext/lib{32,64}/{debug,release}/
* Xref i#277/PR 540817.
*/
privload_add_subdir_path(EXT_SUBDIR);
/* We also support loading from a co-located DRMF package. */
privload_add_subdir_path(DRMF_SUBDIR);
}
/* most uses should call privload_load() instead
* if it fails, unloads
*/
static bool
privload_load_finalize(privmod_t *privmod)
{
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
ASSERT(!privmod->externally_loaded);
privload_add_areas(privmod);
privload_redirect_setup(privmod);
if (!privload_process_imports(privmod)) {
LOG(GLOBAL, LOG_LOADER, 1, "%s: failed to process imports %s\n",
__FUNCTION__, privmod->name);
privload_unload(privmod);
return false;
}
privload_os_finalize(privmod);
if (!privload_call_entry(privmod, DLL_PROCESS_INIT)) {
LOG(GLOBAL, LOG_LOADER, 1, "%s: entry routine failed\n", __FUNCTION__);
privload_unload(privmod);
return false;
}
privload_load_finalized(privmod);
LOG(GLOBAL, LOG_LOADER, 1, "%s: loaded %s @ "PFX"-"PFX" from %s\n", __FUNCTION__,
privmod->name, privmod->base, privmod->base + privmod->size, privmod->path);
return true;
}
static bool
privload_has_thread_entry(void)
{
return IF_UNIX_ELSE(false, true);
}
static bool
privload_modlist_initialized(void)
{
return dynamo_heap_initialized;
}
privmod_t *
privload_next_module(privmod_t *mod)
{
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
return mod->next;
}
privmod_t *
privload_first_module(void)
{
ASSERT_OWN_RECURSIVE_LOCK(true, &privload_lock);
return modlist;
}
/* returns whether they all fit */
bool
privload_print_modules(bool path, bool lock, char *buf, size_t bufsz, size_t *sofar)
{
privmod_t *mod;
if (lock)
acquire_recursive_lock(&privload_lock);
for (mod = modlist; mod != NULL; mod = mod->next) {
if (!mod->externally_loaded)
if (!print_to_buffer(buf, bufsz, sofar, "%s="PFX"\n",
path ? mod->path : mod->name, mod->base)) {
if (lock)
release_recursive_lock(&privload_lock);
return false;
}
}
if (lock)
release_recursive_lock(&privload_lock);
return true;
}