blob: 2c3cdb3023900db9b44562826cdfb25a0bb24016 [file] [log] [blame]
/* *******************************************************************************
* Copyright (c) 2012-2025 Google, Inc. All rights reserved.
* Copyright (c) 2011 Massachusetts Institute of Technology All rights reserved.
* Copyright (c) 2008-2010 VMware, Inc. 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.
*/
#include "../globals.h"
#include "../module_shared.h"
#include "module_private.h"
#include "os_private.h"
#include "../utils.h"
#include "instrument.h"
#include <stddef.h> /* offsetof */
#ifdef LINUX
# include "rseq_linux.h"
#endif
#ifdef NOT_DYNAMORIO_CORE_PROPER
# undef LOG
# define LOG(...) /* nothing */
#else /* !NOT_DYNAMORIO_CORE_PROPER */
/* XXX; perhaps make a module_list interface to check for overlap? */
extern vm_area_vector_t *loaded_module_areas;
void
os_modules_init(void)
{
/* Nothing. */
}
void
os_modules_exit(void)
{
/* Nothing. */
}
/* view_size can be the size of the first mapping, to handle non-contiguous
* modules -- we'll update the module's size here
*/
void
os_module_area_init(module_area_t *ma, app_pc base, size_t view_size, bool at_map,
const char *filepath, uint64 inode HEAPACCT(which_heap_t which))
{
app_pc mod_base, mod_end;
ptr_int_t load_delta;
char *soname = NULL;
ASSERT(module_is_header(base, view_size));
/* i#1589: use privload data if it exists (for client lib) */
if (!privload_fill_os_module_info(base, &mod_base, &mod_end, &soname, &ma->os_data)) {
/* XXX i#1860: on Android we'll fail to fill in info from .dynamic, so
* we'll have incomplete data until the loader maps the segment with .dynamic.
* ma->os_data.have_dynamic_info indicates whether we have the info.
*/
module_walk_program_headers(base, view_size, at_map,
!at_map, /* i#1589: ld.so relocates .dynamic */
&mod_base, NULL, &mod_end, &soname, &ma->os_data);
}
if (ma->os_data.contiguous) {
app_pc map_end = ma->os_data.segments[ma->os_data.num_segments - 1].end;
module_list_add_mapping(ma, base, map_end);
/* update, since may just be 1st segment size */
ma->end = map_end;
} else {
/* Add the non-contiguous segments (i#160/PR 562667). We could just add
* them all separately but vmvectors are more efficient with fewer
* entries so we merge. We don't want general merging in our vector
* either.
*/
app_pc seg_base;
uint i;
ASSERT(ma->os_data.num_segments > 0 && ma->os_data.segments != NULL);
seg_base = ma->os_data.segments[0].start;
for (i = 1; i < ma->os_data.num_segments; i++) {
if (ma->os_data.segments[i].start > ma->os_data.segments[i - 1].end ||
/* XXX: for shared we just add the first one. But if the first
* module is unloaded we'll be missing an entry for the others.
* We assume this won't happen b/c our only use of this now is
* the MacOS dyld shared cache's shared __LINKEDIT segment. If
* it could happen we should switch to a refcount in the vector.
*/
ma->os_data.segments[i - 1].shared) {
if (!ma->os_data.segments[i - 1].shared ||
!vmvector_overlap(loaded_module_areas, seg_base,
ma->os_data.segments[i - 1].end)) {
module_list_add_mapping(ma, seg_base,
ma->os_data.segments[i - 1].end);
}
seg_base = ma->os_data.segments[i].start;
}
}
if (!ma->os_data.segments[i - 1].shared ||
!vmvector_overlap(loaded_module_areas, seg_base,
ma->os_data.segments[i - 1].end))
module_list_add_mapping(ma, seg_base, ma->os_data.segments[i - 1].end);
DOLOG(2, LOG_VMAREAS, {
LOG(GLOBAL, LOG_INTERP | LOG_VMAREAS, 2, "segment list\n");
for (i = 0; i < ma->os_data.num_segments; i++) {
LOG(GLOBAL, LOG_INTERP | LOG_VMAREAS, 2,
"\tsegment %d: [" PFX "," PFX ") prot=%x\n", i,
ma->os_data.segments[i].start, ma->os_data.segments[i].end,
ma->os_data.segments[i].prot);
}
});
/* update to max end (view_size may just be 1st segment end) */
ma->end = ma->os_data.segments[ma->os_data.num_segments - 1].end;
}
# ifdef LINUX
LOG(GLOBAL, LOG_SYMBOLS, 2,
"%s: hashtab=" PFX ", dynsym=" PFX ", dynstr=" PFX ", strsz=" SZFMT
", symsz=" SZFMT "\n",
__func__, ma->os_data.hashtab, ma->os_data.dynsym, ma->os_data.dynstr,
ma->os_data.dynstr_size, ma->os_data.symentry_size);
# endif
# ifdef LINUX /* on Mac the entire dyld shared cache has split __TEXT and __DATA */
/* expect to map whole module */
/* XREF 307599 on rounding module end to the next PAGE boundary */
ASSERT_CURIOSITY(mod_end - mod_base == at_map ? ALIGN_FORWARD(view_size, PAGE_SIZE)
: view_size);
# endif
ma->os_data.base_address = mod_base;
load_delta = base - mod_base;
ma->entry_point = module_entry_point(base, load_delta);
/* names - note os.c callers don't distinguish between no filename and an empty
* filename, we treat both as NULL, but leave the distinction for SONAME. */
if (filepath == NULL || filepath[0] == '\0') {
ma->names.file_name = NULL;
# ifdef VMX86_SERVER
/* XXX: provide a targeted query to avoid full walk */
void *iter = vmk_mmaps_iter_start();
if (iter != NULL) { /* backward compatibility: support lack of iter */
byte *start;
size_t length;
char name[MAXIMUM_PATH];
while (vmk_mmaps_iter_next(iter, &start, &length, NULL, name,
BUFFER_SIZE_ELEMENTS(name))) {
if (base == start) {
ma->names.file_name = dr_strdup(name HEAPACCT(which));
break;
}
}
vmk_mmaps_iter_stop(iter);
}
# endif
ma->full_path = NULL;
} else {
ma->names.file_name = dr_strdup(get_short_name(filepath) HEAPACCT(which));
/* We could share alloc w/ names.file_name but simpler to separate */
ma->full_path = dr_strdup(filepath HEAPACCT(which));
}
ma->names.inode = inode;
if (soname == NULL)
ma->names.module_name = NULL;
else
ma->names.module_name = dr_strdup(soname HEAPACCT(which));
/* Fields for pcaches (PR 295534). These entries are not present in
* all libs: I see DT_CHECKSUM and the prelink field on FC12 but not
* on Ubuntu 9.04.
*/
if (ma->os_data.checksum == 0 &&
(DYNAMO_OPTION(coarse_enable_freeze) || DYNAMO_OPTION(use_persisted))) {
/* Use something so we have usable pcache names */
ma->os_data.checksum = d_r_crc32((const char *)ma->start, PAGE_SIZE);
}
/* Timestamp we just leave as 0 */
# ifdef LINUX
rseq_module_init(ma, at_map);
# endif
}
void
free_module_names(module_names_t *mod_names HEAPACCT(which_heap_t which))
{
ASSERT(mod_names != NULL);
if (mod_names->module_name != NULL)
dr_strfree(mod_names->module_name HEAPACCT(which));
if (mod_names->file_name != NULL)
dr_strfree(mod_names->file_name HEAPACCT(which));
}
void
module_copy_os_data(os_module_data_t *dst, os_module_data_t *src)
{
memcpy(dst, src, sizeof(*dst));
if (src->segments != NULL) {
dst->segments = (module_segment_t *)HEAP_ARRAY_ALLOC(
GLOBAL_DCONTEXT, module_segment_t, src->alloc_segments, ACCT_OTHER,
PROTECTED);
memcpy(dst->segments, src->segments,
src->num_segments * sizeof(module_segment_t));
}
}
void
print_modules(file_t f, bool dump_xml)
{
module_iterator_t *mi;
/* we walk our own module list that is populated on an initial walk through memory,
* and further kept consistent on memory mappings of likely modules */
print_file(f, dump_xml ? "<loaded-modules>\n" : "\nLoaded modules:\n");
mi = module_iterator_start();
while (module_iterator_hasnext(mi)) {
module_area_t *ma = module_iterator_next(mi);
print_file(f,
dump_xml ? "\t<so range=\"" PFX "-" PFX "\" "
"entry=\"" PFX "\" base_address=" PFX "\n"
"\tname=\"%s\" />\n"
: " " PFX "-" PFX " entry=" PFX " base_address=" PFX "\n"
"\tname=\"%s\" \n",
ma->start, ma->end - 1, /* inclusive */
ma->entry_point, ma->os_data.base_address,
GET_MODULE_NAME(&ma->names) == NULL ? "(null)"
: GET_MODULE_NAME(&ma->names));
}
module_iterator_stop(mi);
if (dump_xml)
print_file(f, "</loaded-modules>\n");
else
print_file(f, "\n");
}
void
os_module_area_reset(module_area_t *ma HEAPACCT(which_heap_t which))
{
if (ma->os_data.contiguous)
module_list_remove_mapping(ma, ma->start, ma->end);
else {
/* Remove the non-contiguous segments (i#160/PR 562667) */
app_pc seg_base;
uint i;
ASSERT(ma->os_data.num_segments > 0 && ma->os_data.segments != NULL);
seg_base = ma->os_data.segments[0].start;
for (i = 1; i < ma->os_data.num_segments; i++) {
if (ma->os_data.segments[i].start > ma->os_data.segments[i - 1].end) {
module_list_remove_mapping(ma, seg_base, ma->os_data.segments[i - 1].end);
seg_base = ma->os_data.segments[i].start;
}
}
module_list_remove_mapping(ma, seg_base, ma->os_data.segments[i - 1].end);
}
HEAP_ARRAY_FREE(GLOBAL_DCONTEXT, ma->os_data.segments, module_segment_t,
ma->os_data.alloc_segments, ACCT_OTHER, PROTECTED);
if (ma->full_path != NULL)
dr_strfree(ma->full_path HEAPACCT(which));
}
/* Returns the bounds of the first section with matching name. */
bool
get_named_section_bounds(app_pc module_base, const char *name, app_pc *start /*OUT*/,
app_pc *end /*OUT*/)
{
/* XXX: not implemented */
ASSERT(module_is_header(module_base, 0));
if (start != NULL)
*start = NULL;
if (end != NULL)
*end = NULL;
return false;
}
bool
rct_is_exported_function(app_pc tag)
{
/* XXX: not implemented */
return false;
}
/* TODO PR 295529: NYI, here so code origins policies aren't all ifdef WINDOWS */
const char *
get_module_short_name(app_pc pc HEAPACCT(which_heap_t which))
{
ASSERT_NOT_IMPLEMENTED(false);
return NULL;
}
/* TODO PR 295529: NYI, here so moduledb code isn't all ifdef WINDOWS */
bool
get_module_company_name(app_pc mod_base, char *out_buf, size_t out_buf_size)
{
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
app_pc
get_module_base(app_pc pc)
{
app_pc base = NULL;
module_area_t *ma;
os_get_module_info_lock();
ma = module_pc_lookup(pc);
if (ma != NULL)
base = ma->start;
os_get_module_info_unlock();
return base;
}
/* TODO PR 212458: NYI, here so code origins policies aren't all ifdef WINDOWS */
bool
is_range_in_code_section(app_pc module_base, app_pc start_pc, app_pc end_pc,
app_pc *sec_start /* OUT */, app_pc *sec_end /* OUT */)
{
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
/* TODO PR 212458: NYI, here so code origins policies aren't all ifdef WINDOWS */
bool
is_in_code_section(app_pc module_base, app_pc addr, app_pc *sec_start /* OUT */,
app_pc *sec_end /* OUT */)
{
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
/* TODO PR 212458: NYI, here so code origins policies aren't all ifdef WINDOWS */
bool
is_in_dot_data_section(app_pc module_base, app_pc addr, app_pc *sec_start /* OUT */,
app_pc *sec_end /* OUT */)
{
return false;
ASSERT_NOT_IMPLEMENTED(false);
}
/* TODO PR 212458: NYI, here so code origins policies aren't all ifdef WINDOWS */
bool
is_in_any_section(app_pc module_base, app_pc addr, app_pc *sec_start /* OUT */,
app_pc *sec_end /* OUT */)
{
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
bool
is_mapped_as_image(app_pc module_base)
{
return module_is_header(module_base, 0);
}
/* Gets module information of module containing pc, cached from our module list.
* Returns false if not in module; none of the OUT arguments are set in that case.
*
* XXX: share code w/ win32/module.c's os_get_module_info()
*
* Note: this function returns only one module name using the rule established
* by GET_MODULE_NAME(); for getting all possible ones use
* os_get_module_info_all_names() directly. Part of fix for case 9842.
*
* If name != NULL, caller must acquire the module_data_lock beforehand
* and call os_get_module_info_unlock() when finished with the name
* (caller can use dr_strdup to make a copy first if necessary; validity of the
* name is guaranteed only as long as the caller holds module_data_lock).
* If name == NULL, this routine acquires and releases the lock and the
* caller has no obligations.
*/
bool
os_get_module_info(const app_pc pc, uint *checksum, uint *timestamp, size_t *size,
const char **name, size_t *code_size, uint64 *file_version)
{
module_area_t *ma;
if (!is_module_list_initialized())
return false;
/* read lock to protect custom data */
if (name == NULL)
os_get_module_info_lock();
ASSERT(os_get_module_info_locked());
;
ma = module_pc_lookup(pc);
if (ma != NULL) {
if (checksum != NULL)
*checksum = ma->os_data.checksum;
if (timestamp != NULL)
*timestamp = ma->os_data.timestamp;
if (size != NULL)
*size = ma->end - ma->start;
if (name != NULL)
*name = GET_MODULE_NAME(&ma->names);
if (code_size != NULL) {
/* Using rx segment size since don't want to impl section
* iterator (i#76/PR 212458)
*/
uint i;
size_t rx_sz = 0;
ASSERT(ma->os_data.num_segments > 0 && ma->os_data.segments != NULL);
for (i = 0; i < ma->os_data.num_segments; i++) {
if (ma->os_data.segments[i].prot == (MEMPROT_EXEC | MEMPROT_READ)) {
rx_sz = ma->os_data.segments[i].end - ma->os_data.segments[i].start;
break;
}
}
*code_size = rx_sz;
}
if (file_version != NULL) {
/* TODO: NYI: make windows-only everywhere if no good linux source */
*file_version = 0;
}
}
if (name == NULL)
os_get_module_info_unlock();
return (ma != NULL);
}
bool
os_get_module_info_all_names(const app_pc pc, uint *checksum, uint *timestamp,
size_t *size, module_names_t **names, size_t *code_size,
uint64 *file_version)
{
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
# if defined(RETURN_AFTER_CALL) || defined(RCT_IND_BRANCH)
extern rct_module_table_t rct_global_table;
/* Caller must hold module_data_lock */
rct_module_table_t *
os_module_get_rct_htable(app_pc pc, rct_type_t which)
{
/* XXX: until we have a module list we use global rct and rac tables */
if (which == RCT_RCT)
return &rct_global_table;
return NULL; /* we use rac_non_module_table */
}
# endif
/* Adds an entry for a segment to the out_data->segments array */
void
module_add_segment_data(DR_PARAM_OUT os_module_data_t *out_data,
uint num_segments /*hint only*/, app_pc segment_start,
size_t segment_size, uint segment_prot, /* MEMPROT_ */
size_t alignment, bool shared, uint64 offset)
{
uint seg, i;
LOG(GLOBAL, LOG_INTERP | LOG_VMAREAS, 3, "%s: #=%d " PFX "-" PFX " 0x%x\n",
__FUNCTION__, out_data->num_segments, segment_start, segment_start + segment_size,
segment_prot);
if (out_data->alignment == 0) {
out_data->alignment = alignment;
} else {
/* We expect all segments to have the same alignment for ELF. */
IF_LINUX(ASSERT_CURIOSITY(out_data->alignment == alignment));
}
/* Add segments to the module vector (i#160/PR 562667).
* For !HAVE_MEMINFO we should combine w/ the segment
* walk done in dl_iterate_get_areas_cb().
*/
if (out_data->num_segments + 1 >= out_data->alloc_segments) {
/* over-allocate to avoid 2 passes to count PT_LOAD */
uint newsz;
module_segment_t *newmem;
if (out_data->alloc_segments == 0)
newsz = (num_segments == 0 ? 4 : num_segments);
else
newsz = out_data->alloc_segments * 2;
newmem = (module_segment_t *)HEAP_ARRAY_ALLOC(GLOBAL_DCONTEXT, module_segment_t,
newsz, ACCT_OTHER, PROTECTED);
if (out_data->alloc_segments > 0) {
memcpy(newmem, out_data->segments,
out_data->alloc_segments * sizeof(*out_data->segments));
HEAP_ARRAY_FREE(GLOBAL_DCONTEXT, out_data->segments, module_segment_t,
out_data->alloc_segments, ACCT_OTHER, PROTECTED);
}
out_data->segments = newmem;
out_data->alloc_segments = newsz;
out_data->contiguous = true;
}
/* Keep array sorted in addr order. I'm assuming segments are disjoint! */
for (i = 0; i < out_data->num_segments; i++) {
if (out_data->segments[i].start > segment_start)
break;
}
seg = i;
/* Shift remaining entries */
for (i = out_data->num_segments; i > seg; i--) {
out_data->segments[i] = out_data->segments[i - 1];
}
out_data->num_segments++;
ASSERT(out_data->num_segments <= out_data->alloc_segments);
# ifdef MACOS
/* Some libraries have sub-page segments so do not page-align. We assume
* these are already aligned.
*/
out_data->segments[seg].start = segment_start;
out_data->segments[seg].end = segment_start + segment_size;
# else
/* ELF requires p_vaddr to already be aligned to p_align */
out_data->segments[seg].start = (app_pc)ALIGN_BACKWARD(segment_start, PAGE_SIZE);
out_data->segments[seg].end =
(app_pc)ALIGN_FORWARD(segment_start + segment_size, PAGE_SIZE);
# endif
out_data->segments[seg].prot = segment_prot;
out_data->segments[seg].shared = shared;
out_data->segments[seg].offset = offset;
if (seg > 0) {
ASSERT(out_data->segments[seg].start >= out_data->segments[seg - 1].end);
if (out_data->segments[seg].start > out_data->segments[seg - 1].end)
out_data->contiguous = false;
}
if (seg < out_data->num_segments - 1) {
ASSERT(out_data->segments[seg + 1].start >= out_data->segments[seg].end);
if (out_data->segments[seg + 1].start > out_data->segments[seg].end)
out_data->contiguous = false;
}
}
/* Returns true if the module has an nth segment, false otherwise. */
bool
module_get_nth_segment(app_pc module_base, uint n, app_pc *start /*OPTIONAL OUT*/,
app_pc *end /*OPTIONAL OUT*/, uint *chars /*OPTIONAL OUT*/)
{
module_area_t *ma;
bool res = false;
if (!is_module_list_initialized())
return false;
os_get_module_info_lock();
ma = module_pc_lookup(module_base);
if (ma != NULL && n < ma->os_data.num_segments) {
LOG(GLOBAL, LOG_INTERP | LOG_VMAREAS, 3, "%s: [" PFX "-" PFX ") %x\n",
__FUNCTION__, ma->os_data.segments[n].start, ma->os_data.segments[n].end,
ma->os_data.segments[n].prot);
if (start != NULL)
*start = ma->os_data.segments[n].start;
if (end != NULL)
*end = ma->os_data.segments[n].end;
if (chars != NULL)
*chars = ma->os_data.segments[n].prot;
res = true;
}
os_get_module_info_unlock();
return res;
}
#endif /* !NOT_DYNAMORIO_CORE_PROPER */
/* XXX: We could implement import iteration of PE files in Wine, so we provide
* these stubs.
*/
dr_module_import_iterator_t *
dr_module_import_iterator_start(module_handle_t handle)
{
CLIENT_ASSERT(false,
"No imports on Linux, use "
"dr_symbol_import_iterator_t instead");
return NULL;
}
bool
dr_module_import_iterator_hasnext(dr_module_import_iterator_t *iter)
{
return false;
}
dr_module_import_t *
dr_module_import_iterator_next(dr_module_import_iterator_t *iter)
{
return NULL;
}
void
dr_module_import_iterator_stop(dr_module_import_iterator_t *iter)
{
}
bool
at_dl_runtime_resolve_ret(dcontext_t *dcontext, app_pc source_fragment, int *ret_imm)
{
/* source_fragment is the start pc of the fragment to be run under DR
* offset is the location of the address dl_runtime_resolve
* will return to, relative to xsp
*/
/* It works for the UNIX loader hack in _dl_runtime_resolve */
/* The offending sequence in ld-linux.so is
* <_dl_runtime_resolve>:
* c270: 5a pop %edx
* c271: 59 pop %ecx
* c272: 87 04 24 xchg %eax,(%esp)
* c275: c2 08 00 ret $0x8
*/
/* The same code also is in 0000c280 <_dl_runtime_profile>
* It maybe that either one or the other is ever used.
* Although performancewise this pattern matching is very cheap,
* for stricter security we assume only one is used in a session.
*/
/* XXX: This may change with future versions of libc, tested on
* RH8 and RH9 only. Also works for whatever libc was in ubuntu 7.10.
*/
/* However it does not work for ubuntu 8.04 where the code sequence has
* changed to the still similar :
* 2c50: 5a pop %edx
* 2c51: 8b 0c 24 mov (%esp) -> %ecx
* 2c54: 89 04 24 mov %eax -> (%esp)
* 2c57: 8b 44 24 04 mov 0x04(%esp) -> %eax
* 2c5b: c2 0c 00 ret $0xc
* So we check for that sequence too.
*/
static const byte DL_RUNTIME_RESOLVE_MAGIC_1[8] =
/* pop edx, pop ecx; xchg eax, (esp) ret 8 */
{ 0x5a, 0x59, 0x87, 0x04, 0x24, 0xc2, 0x08, 0x00 };
static const byte DL_RUNTIME_RESOLVE_MAGIC_2[14] =
/* pop edx, mov (esp)->ecx, mov eax->(esp), mov 4(esp)->eax, ret 12 */
{ 0x5a, 0x8b, 0x0c, 0x24, 0x89, 0x04, 0x24,
0x8b, 0x44, 0x24, 0x04, 0xc2, 0x0c, 0x00 };
byte buf[MAX(sizeof(DL_RUNTIME_RESOLVE_MAGIC_1),
sizeof(DL_RUNTIME_RESOLVE_MAGIC_2))] = { 0 };
if (d_r_safe_read(source_fragment, sizeof(DL_RUNTIME_RESOLVE_MAGIC_1), buf) &&
memcmp(buf, DL_RUNTIME_RESOLVE_MAGIC_1, sizeof(DL_RUNTIME_RESOLVE_MAGIC_1)) ==
0) {
*ret_imm = 0x8;
return true;
}
if (d_r_safe_read(source_fragment, sizeof(DL_RUNTIME_RESOLVE_MAGIC_2), buf) &&
memcmp(buf, DL_RUNTIME_RESOLVE_MAGIC_2, sizeof(DL_RUNTIME_RESOLVE_MAGIC_2)) ==
0) {
LOG(THREAD, LOG_INTERP, 1,
"RCT: KNOWN exception this is "
"_dl_runtime_resolve --ok \n");
*ret_imm = 0xc;
return true;
}
return false;
}
bool
module_file_is_module64(file_t f, bool *is64 DR_PARAM_OUT, bool *also32 DR_PARAM_OUT)
{
dr_platform_t platform, alt_platform;
if (module_get_platform(f, &platform, &alt_platform)) {
*is64 = (platform == DR_PLATFORM_64BIT);
if (also32 != NULL)
*also32 = (platform == DR_PLATFORM_32BIT);
return true;
}
return false;
}
bool
module_contains_addr(module_area_t *ma, app_pc pc)
{
if (ma->os_data.contiguous)
return (pc >= ma->start && pc < ma->end);
else {
uint i;
ASSERT(ma->os_data.num_segments > 0 && ma->os_data.segments != NULL);
for (i = 0; i < ma->os_data.num_segments; i++) {
if (pc >= ma->os_data.segments[i].start && pc < ma->os_data.segments[i].end)
return true;
}
}
return false;
}