static void event_thread_init(void *drcontext) { per_thread_t *data = dr_thread_alloc(drcontext, sizeof(*data)); DR_ASSERT(data != NULL); dr_set_tls_field(drcontext, data); /* Keep seg_base in a per-thread data structure so we can get the TLS * slot and find where the pointer points to in the buffer. * It is mainly for users using a debugger to get the execution history. */ data->seg_base = dr_get_dr_segment_base(tls_seg); /* We allocate a 128KB buffer to make sure we have a 64KB buffer with * 64KB-aligned starting address, so that we can fill the buffer * cyclically by incrementing the bottom 16 bits of the pointer. */ data->buf_base = dr_raw_mem_alloc(TLS_BUF_SIZE, DR_MEMPROT_READ | DR_MEMPROT_WRITE, NULL); DR_ASSERT(data->seg_base != NULL && data->buf_base != NULL); memset(data->buf_base, 0, TLS_BUF_SIZE); /* put the 64KB-aligned address into TLS slot as the pointer pointing * to the 64KB cyclic buffer */ *(void **)((byte *)(data->seg_base) + tls_offs) = (void *) ALIGN_FORWARD(data->buf_base, BUF_64K_BYTE); }
static bool exception_event_redirect(void *dcontext, dr_exception_t *excpt) { app_pc addr; dr_mcontext_t mcontext = {sizeof(mcontext),DR_MC_ALL,}; module_data_t *data = dr_lookup_module_by_name("client.events.exe"); dr_fprintf(STDERR, "exception event redirect\n"); if (data == NULL) { dr_fprintf(STDERR, "couldn't find events.exe module\n"); return true; } addr = (app_pc)dr_get_proc_address(data->handle, "redirect"); dr_free_module_data(data); mcontext = *excpt->mcontext; mcontext.pc = addr; if (addr == NULL) { dr_fprintf(STDERR, "Couldn't find function redirect in events.exe\n"); return true; } #ifdef X64 /* align properly in case redirect function relies on conventions (i#419) */ mcontext.xsp = ALIGN_FORWARD(mcontext.xsp, 16) - 8; #endif dr_redirect_execution(&mcontext); dr_fprintf(STDERR, "should not be reached, dr_redirect_execution() should not return\n"); return true; }
static uint hash_key(hashtable_t *table, void *key) { uint hash = 0; if (table->hash_key_func != NULL) { hash = table->hash_key_func(key); } else if (table->hashtype == HASH_STRING || table->hashtype == HASH_STRING_NOCASE) { const char *s = (const char *) key; char c; uint i, shift; uint max_shift = ALIGN_FORWARD(table->table_bits, 8); /* XXX: share w/ core's hash_value() function */ for (i = 0; s[i] != '\0'; i++) { c = s[i]; if (table->hashtype == HASH_STRING_NOCASE) c = (char) tolower(c); shift = (i % 4) * 8; if (shift > max_shift) shift = max_shift; hash ^= c << shift; } } else { /* HASH_INTPTR, or fallback for HASH_CUSTOM in release build */ ASSERT(table->hashtype == HASH_INTPTR, "hashtable.c hash_key internal error: invalid hash type"); hash = (uint)(ptr_uint_t) key; } return HASH_FUNC_BITS(hash, table->table_bits); }
uint CDynamoRIOView::PrintClientStats(TCHAR *c, TCHAR *max) { if (m_clientStats == NULL) return 0; #define CLIENTSTAT_NAME_MAX_SHOW CLIENTSTAT_NAME_MAX_LEN uint i; TCHAR *start = c; char(*names)[CLIENTSTAT_NAME_MAX_LEN] = (char(*)[CLIENTSTAT_NAME_MAX_LEN])m_clientStats->data; stats_int_t *vals = (stats_int_t *)((char *)m_clientStats->data + m_clientStats->num_stats * CLIENTSTAT_NAME_MAX_LEN * sizeof(char)); /* account for struct alignment */ vals = (stats_int_t *)ALIGN_FORWARD(vals, sizeof(stats_int_t)); for (i = 0; i < m_clientStats->num_stats; i++) { if (c >= max - CLIENTSTAT_NAME_MAX_SHOW * 2 - 3) break; c += _stprintf(c, _T("%*.*") ASCII_PRINTF _T(" = ") CLIENT_STAT_PFMT _T("\r\n"), CLIENTSTAT_NAME_MAX_SHOW, CLIENTSTAT_NAME_MAX_SHOW, names[i], vals[i]); assert(c < max); } return (uint)(c - start); }
bool get_named_section_bounds(byte *module_base, const char *name, byte **start/*OUT*/, byte **end/*OUT*/) { IMAGE_DOS_HEADER *dos; IMAGE_NT_HEADERS *nt; IMAGE_SECTION_HEADER *sec; uint i; bool prev_sec_same_chars = false; assert(module_base != NULL); dos = (IMAGE_DOS_HEADER *) module_base; if (dos->e_magic != IMAGE_DOS_SIGNATURE) return false; nt = (IMAGE_NT_HEADERS *) (((ptr_uint_t)dos) + dos->e_lfanew); if (nt == NULL || nt->Signature != IMAGE_NT_SIGNATURE) return false; assert(start != NULL || end != NULL); sec = IMAGE_FIRST_SECTION(nt); for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { if (sec->Name != NULL && strncmp(sec->Name, name, strlen(name)) == 0) { if (start != NULL) *start = module_base + sec->VirtualAddress; if (end != NULL) *end = module_base + sec->VirtualAddress + ALIGN_FORWARD(sec->Misc.VirtualSize, PAGE_SIZE); return true; } sec++; } return false; }
void privload_tls_exit(void *dr_tp) { byte *alloc; if (dr_tp == NULL || dr_tp == init_thread.tls) return; alloc = (byte *)dr_tp - offsetof(android_pthread_internal_t, tls); heap_munmap(alloc, ALIGN_FORWARD(sizeof(android_pthread_internal_t), PAGE_SIZE)); }
void dc_invalidaterange(void *start, u32 size) { u32 cookie = irq_kill(); void *end = ALIGN_FORWARD(((u8*)start) + size, LINESIZE); start = ALIGN_BACKWARD(start, LINESIZE); _dc_inval_entries(start, (end - start) / LINESIZE); ahb_flush_to(AHB_STARLET); irq_restore(cookie); }
app_pc umbra_align_cache_line(app_pc pc) { app_pc new_pc; new_pc = (app_pc)ALIGN_FORWARD(pc, proc_get_cache_line_size()); SET_TO_NOPS(pc, new_pc - pc); return new_pc; }
void dc_flushrange(const void *start, u32 size) { u32 cookie = irq_kill(); if(size > 0x4000) { _dc_flush(); } else { void *end = ALIGN_FORWARD(((u8*)start) + size, LINESIZE); start = ALIGN_BACKWARD(start, LINESIZE); _dc_flush_entries(start, (end - start) / LINESIZE); } _drain_write_buffer(); ahb_flush_from(AHB_1); irq_restore(cookie); }
app_pc get_heap_start(void) { static app_pc heap_start; /* cached value */ if (heap_start == NULL) { app_pc cur_brk = get_brk(true/*pre-us*/); dr_mem_info_t info; module_data_t *data; /* Locate the heap */ if (!dr_query_memory_ex(cur_brk - 1, &info)) { ASSERT(false, "cannot find heap region"); return NULL; } if (info.type == DR_MEMTYPE_FREE || info.type == DR_MEMTYPE_IMAGE || !TEST(DR_MEMPROT_WRITE, info.prot)) { /* Heap is empty */ heap_start = cur_brk; } else { ASSERT(!dr_memory_is_dr_internal(info.base_pc), "heap location error"); /* we no longer assert that these are equal b/c -replace_malloc * has extended the brk already */ ASSERT(info.base_pc + info.size >= cur_brk, "heap location error"); heap_start = info.base_pc; /* workaround for PR 618178 where /proc/maps is wrong on suse * and lists last 2 pages of executable as heap! */ /* On some old Linux kernel, the heap might be right after the bss * segment. DR's map iterator used by dr_query_memory_ex cannot * split bss out of heap. * We use dr_lookup_module to find the right bounds of bss so that * we can check whether the base is bss, existing heap, or merge of * the two. */ /* XXX: we still cannot handle the case that the application creates * memory right before the heap. */ data = dr_lookup_module(info.base_pc); if (data != NULL) { if (data->start < heap_start && data->end > heap_start) { heap_start = (byte *) ALIGN_FORWARD(data->end, PAGE_SIZE); LOG(1, "WARNING: workaround for invalid heap_start "PFX" => "PFX"\n", info.base_pc, heap_start); } dr_free_module_data(data); } } } return heap_start; }
/* * allocate_memory(): Simple memory allocation routine */ void_t *allocate_memory(uint64_t size_request) { uint64_t address; if (heap_current + size_request > heap_tops) { PRINT_STRING("Allocation request exceeds heap's size\r\n"); PRINT_STRING_AND_VALUE("Heap current = 0x", heap_current); PRINT_STRING_AND_VALUE("Requested size = 0x", size_request); PRINT_STRING_AND_VALUE("Heap tops = 0x", heap_tops); return NULL; } address = ALIGN_FORWARD(heap_current, MEM_ALLOCATE_ALIGNMENT); heap_current += size_request; zero_mem((void_t *)address, size_request); return (void_t *)address; }
bool handle_mem_ref(uint flags, app_loc_t *loc, byte *addr, size_t sz, dr_mcontext_t *mc) { byte *ptr; /* We're piggybacking on Dr. Memory syscall, etc. code. For reads * and writes we want to mark the shadow byte to indicate the * memory was accessed. For an addressability check we do * nothing. */ if (TEST(MEMREF_CHECK_ADDRESSABLE, flags)) return true; /* We ignore MEMREF_MOVS, etc.: we don't propagate anything */ for (ptr = (byte *) ALIGN_BACKWARD(addr, SHADOW_GRANULARITY); ptr < (byte *) ALIGN_FORWARD(addr + sz, SHADOW_GRANULARITY); ptr += SHADOW_GRANULARITY) { shadow_set_byte(ptr, 1); } return true; }
void *CDECL mon_page_alloc(uint64_t pages) { uint64_t address; uint64_t size = pages * PAGE_SIZE; address = ALIGN_FORWARD(heap_current, PAGE_SIZE); if (address + size > heap_tops) { PRINT_STRING("Allocation request exceeds heap's size\r\n"); PRINT_STRING_AND_VALUE("Page aligned heap current = 0x", address); PRINT_STRING_AND_VALUE("Requested size = 0x", size); PRINT_STRING_AND_VALUE("Heap tops = 0x", heap_tops); return NULL; } heap_current = address + size; zero_mem((void *)address, size); return (void *)address; }
/* pass non-NULL for thandle if you want this routine to use * Get/SetThreadContext to get the context -- you must still pass * in a pointer to a cxt */ BOOL inject_into_thread(HANDLE phandle, CONTEXT *cxt, HANDLE thandle, char *dynamo_path) { size_t nbytes; BOOL success = FALSE; ptr_uint_t dynamo_entry_esp; ptr_uint_t dynamo_path_esp; LPVOID load_dynamo_code = NULL; /* = base of code allocation */ ptr_uint_t addr; reg_t *bufptr; char buf[MAX_PATH]; uint old_prot; ASSERT(cxt != NULL); #ifndef NOT_DYNAMORIO_CORE_PROPER /* FIXME - if we were early injected we couldn't call inject_init during * startup because kernel32 wasn't loaded yet, so we call it here which * isn't safe because it uses app locks. If we want to support a mix * of early and late follow children injection we should change load_dynamo * to use Nt functions (which we can link) rather then kernel32 functions * (which we have to look up). We could also use module.c code to safely * walk the exports of kernel32.dll (we can cache its mod handle when it * is loaded). */ if (!inject_initialized) { SYSLOG_INTERNAL_WARNING("Using late inject follow children from early injected process, unsafe LdrLock usage"); SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT); inject_init(); SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT); } #else ASSERT(inject_initialized); #endif /* soon we'll start using alternative injection with case 102 - leaving block */ { reg_t app_xsp; if (thandle != NULL) { /* grab the context of the app's main thread */ cxt->ContextFlags = CONTEXT_DR_STATE; if (!NT_SUCCESS(nt_get_context(thandle, cxt))) { display_error("GetThreadContext failed"); goto error; } } app_xsp = cxt->CXT_XSP; /* copy load_dynamo() into the address space of the new process */ ASSERT(BUFFER_SIZE_BYTES(buf) > SIZE_OF_LOAD_DYNAMO); memcpy(buf, (char*)load_dynamo, SIZE_OF_LOAD_DYNAMO); /* R-X protection is adequate for our non-self modifying code, * and we'll update that after we're done with * nt_write_virtual_memory() calls */ /* get allocation, this will be freed by os_heap_free, so make sure * is compatible allocation method */ if (!NT_SUCCESS(nt_remote_allocate_virtual_memory(phandle, &load_dynamo_code, SIZE_OF_LOAD_DYNAMO, PAGE_EXECUTE_READWRITE, MEMORY_COMMIT))) { display_error("Failed to allocate memory for injection code"); goto error; } if (!nt_write_virtual_memory(phandle, load_dynamo_code, buf, SIZE_OF_LOAD_DYNAMO, &nbytes)) { display_error("WriteMemory failed"); goto error; } /* Xref PR 252745 & PR 252008 - we can use the app's stack to hold our data * even on WOW64 and 64-bit since we're using set context to set xsp. */ /* copy the DYNAMORIO_ENTRY string to the app's stack */ _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s", DYNAMORIO_ENTRY); NULL_TERMINATE_BUFFER(buf); nbytes = strlen(buf) + 1; // include the trailing '\0' /* keep esp at pointer-sized alignment */ cxt->CXT_XSP -= ALIGN_FORWARD(nbytes, XSP_SZ); dynamo_entry_esp = cxt->CXT_XSP; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, buf, nbytes, &nbytes)) { display_error("WriteMemory failed"); goto error; } /* copy the dynamorio_path string to the app's stack */ _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s", dynamo_path); NULL_TERMINATE_BUFFER(buf); nbytes = strlen(buf) + 1; // include the trailing '\0' /* keep esp at pointer-sized byte alignment */ cxt->CXT_XSP -= ALIGN_FORWARD(nbytes, XSP_SZ); dynamo_path_esp = cxt->CXT_XSP; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, buf, nbytes, &nbytes)) { display_error("WriteMemory failed"); goto error; } /* copy the current context to the app's stack. Only need the * control registers, so we use a dr_mcontext_t layout. */ bufptr = (reg_t*) buf; *bufptr++ = cxt->CXT_XDI; *bufptr++ = cxt->CXT_XSI; *bufptr++ = cxt->CXT_XBP; *bufptr++ = app_xsp; *bufptr++ = cxt->CXT_XBX; *bufptr++ = cxt->CXT_XDX; *bufptr++ = cxt->CXT_XCX; *bufptr++ = cxt->CXT_XAX; #ifdef X64 *bufptr++ = cxt->R8; *bufptr++ = cxt->R9; *bufptr++ = cxt->R10; *bufptr++ = cxt->R11; *bufptr++ = cxt->R12; *bufptr++ = cxt->R13; *bufptr++ = cxt->R14; *bufptr++ = cxt->R15; #endif /* It would be nice to use preserve_xmm_caller_saved(), but we'd need to * link proc.c and deal w/ messy dependencies to get it into arch_exports.h, * so we do our own check. We go ahead and put in the xmm slots even * if the underlying processor has no xmm support: no harm done. */ if (IF_X64_ELSE(true, is_wow64_process(NT_CURRENT_PROCESS))) { /* PR 264138: preserve xmm0-5. We fill in all slots even though * for 32-bit we don't use them (PR 306394). */ int i, j; for (i = 0; i < NUM_XMM_SLOTS; i++) { for (j = 0; j < IF_X64_ELSE(2,4); j++) { *bufptr++ = CXT_XMM(cxt, i)->reg[j]; } } } else { /* skip xmm slots */ bufptr += XMM_SLOTS_SIZE/sizeof(*bufptr); } *bufptr++ = cxt->CXT_XFLAGS; *bufptr++ = cxt->CXT_XIP; ASSERT((char *)bufptr - (char *)buf == sizeof(dr_mcontext_t)); *bufptr++ = (ptr_uint_t)load_dynamo_code; *bufptr++ = SIZE_OF_LOAD_DYNAMO; nbytes = sizeof(dr_mcontext_t) + 2*sizeof(reg_t); cxt->CXT_XSP -= nbytes; #ifdef X64 /* We need xsp to be aligned prior to each call, but we can only pad * before the context as all later users assume the info they need is * at TOS. */ cxt->CXT_XSP = ALIGN_BACKWARD(cxt->CXT_XSP, XMM_ALIGN); #endif if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, buf, nbytes, &nbytes)) { display_error("WriteMemory failed"); goto error; } /* push the address of the DYNAMORIO_ENTRY string on the app's stack */ cxt->CXT_XSP -= XSP_SZ; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, &dynamo_entry_esp, sizeof(dynamo_entry_esp), &nbytes)) { display_error("WriteMemory failed"); goto error; } /* push the address of GetProcAddress on the app's stack */ ASSERT(addr_getprocaddr); addr = addr_getprocaddr; cxt->CXT_XSP -= XSP_SZ; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, &addr, sizeof(addr), &nbytes)) { display_error("WriteMemory failed"); goto error; } /* push the address of the dynamorio_path string on the app's stack */ cxt->CXT_XSP -= XSP_SZ; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, &dynamo_path_esp, sizeof(dynamo_path_esp), &nbytes)) { display_error("WriteMemory failed"); goto error; } /* push the address of LoadLibraryA on the app's stack */ ASSERT(addr_loadlibrarya); addr = addr_loadlibrarya; cxt->CXT_XSP -= XSP_SZ; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, &addr, sizeof(addr), &nbytes)) { display_error("WriteMemory failed"); goto error; } #ifdef LOAD_DYNAMO_DEBUGBREAK /* push the address of DebugBreak on the app's stack */ ASSERT(addr_debugbreak); addr = addr_debugbreak; cxt->CXT_XSP -= XSP_SZ; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, &addr, sizeof(addr), &nbytes)) { display_error("WriteMemory failed"); goto error; } #endif /* make the code R-X now */ if (!nt_remote_protect_virtual_memory(phandle, load_dynamo_code, SIZE_OF_LOAD_DYNAMO, PAGE_EXECUTE_READ, &old_prot)) { display_error("Failed to make injection code R-X"); goto error; } ASSERT(old_prot == PAGE_EXECUTE_READWRITE); /* now change Eip to point to the entry point of load_dynamo(), so that when we resume, load_dynamo is invoked automatically */ cxt->CXT_XIP = (ptr_uint_t)load_dynamo_code; cxt->CXT_XFLAGS = 0; if (thandle != NULL) { if (!NT_SUCCESS(nt_set_context(thandle, cxt))) { display_error("SetThreadContext failed"); goto error; } } success = TRUE; } error: /* we do not recover any changes in the child's address space */ return success; }
static UINT32 ipc_get_message_array_list_size(UINT32 number_of_host_processors) { return (UINT32) ALIGN_FORWARD(array_list_memory_size( NULL, sizeof(IPC_MESSAGE), ipc_get_max_pending_messages(number_of_host_processors), IPC_ALIGNMENT), IPC_ALIGNMENT); }
bool compare_pages(void *drcontext, byte *start1, byte *start2, uint start_offset) { static byte copy_buf1_i[2*PAGE_SIZE+100]; static byte copy_buf2_i[2*PAGE_SIZE+100]; static byte buf1_i[2*PAGE_SIZE+100]; static byte buf2_i[2*PAGE_SIZE+100]; byte *copy1 = (byte *)ALIGN_FORWARD(copy_buf1_i, PAGE_SIZE); byte *copy2 = (byte *)ALIGN_FORWARD(copy_buf2_i, PAGE_SIZE); byte *buf1 = (byte *)ALIGN_FORWARD(buf1_i, PAGE_SIZE); byte *buf2 = (byte *)ALIGN_FORWARD(buf2_i, PAGE_SIZE); byte *p1 = copy1+start_offset, *p2 = copy2+start_offset; byte *b1 = buf1, *c1 = buf1, *b2 = buf2, *c2 = buf2; int skipped_bytes1 = 0, skipped_identical_bytes1 = 0; int skipped_bytes2 = 0, skipped_identical_bytes2 = 0; /* we make a copy (zero extend the page) for decode to avoid walking to next, * potentially invalid page */ memcpy(copy1, start1, PAGE_SIZE); memcpy(copy2, start2, PAGE_SIZE); /* Note - we do the compare ~1 instruction at a time, would be more effiecient * to do the whole page and compare, but this makes it easier to track down where * the differences originate from. */ while (p1 < copy1 + PAGE_SIZE) { /* The idea is to do a lightweight decoding of the page and eliminate likely * relocations from the instruction stream. I expect that relocations within * the stream to be 4-byte immeds or 4-byte displacements with pointer like * values. For now just using decode_sizeof to get the size of the instuction * and based on that determine how much to chop off the end (instruction format * is [...][disp][imm]) to remove the potential relocation. With better * information from decode_sizeof (it knows whether an immed/disp is present or * not and what offset it would be etc.) we could keep more of the bytes but * this works for testing. What we'll miss unless we get really lucky is * relocs in read only data (const string arrays, for dr the const decode * tables full of pointers for ex.). */ /* How the assumptions work out. The assumption that we quickly get to the * appropriate instruction frame seems valid. Most cases we synchronize very * quickly, usually just a couple of bytes and very rarely more then 20. * I Haven't seen any relocations in instructions that weren't caught * below. However, so far only matching ~60% of sibling pages because of * read only data relocations interspersed in the text sections. Instruction * frame alignment isn't an issue that often and the second pass is catching * most of those. */ while (p1 - copy1 <= p2 - copy2) { uint num_bytes, i, num_prefix = 0; uint size = decode_sizeof(drcontext, p1, &num_prefix); size -= num_prefix; num_bytes = num_prefix; if (size == 0) { size = 1; /* treat invalid as size == 1 */ num_bytes += 1; } else if (size <= 4) { num_bytes += size; /* no 4-byte disp or imm */ } else if (size <= 6 ) { num_bytes += size - 4; /* could have 4-byte disp or immed */ } else if (size <= 7) { num_bytes += size - 5; /* could have 4-byte disp and upto 1-byte immed * or 4-byte immed */ } else if (size <= 9) { num_bytes += size - 6; /* could have 4-byte disp and upto 2-byte immed * or 4-byte immed */ } else { num_bytes += size - 8; /* could have 4-byte disp and 4-byte immed */ } for (i = 0; i < num_bytes; i++) *b1++ = *p1++; skipped_bytes1 += size - (num_bytes - num_prefix); for (i = 0; i < size - (num_bytes - num_prefix); i++) { if (*p1 == *(copy2+(p1-copy1))) skipped_identical_bytes1++; p1++; } } while (p2 - copy2 < p1 - copy1) { uint num_bytes, i, num_prefix = 0; uint size = decode_sizeof(drcontext, p2, &num_prefix); size -= num_prefix; num_bytes = num_prefix; if (size == 0) { size = 1; /* treat invalid as size == 1 */ num_bytes += 1; } else if (size <= 4) { num_bytes += size; } else if (size <=6 ) { num_bytes += size - 4; } else if (size <= 7) { num_bytes += size - 5; } else if (size <= 9) { num_bytes += size - 6; } else { num_bytes += size - 8; } for (i = 0; i < num_bytes; i++) *b2++ = *p2++; skipped_bytes2 += size - (num_bytes - num_prefix); for (i = 0; i < size - (num_bytes - num_prefix); i++) { if (*p2 == *(copy1+(p2-copy2))) skipped_identical_bytes2++; p2++; } } /* Do check */ while (c1 < b1 && c2 < b2) { if (*c1++ != *c2++) { VVERBOSE_PRINT("Mismatch found near offset 0x%04x of page %08x\n", p1 - copy1, start1); return false; } } } ASSERT(skipped_bytes1 == skipped_bytes2); ASSERT(skipped_identical_bytes1 == skipped_identical_bytes2); VVERBOSE_PRINT("Match found! skipped=%d skipped_identical=%d\n", skipped_bytes1, skipped_identical_bytes1); return true; }
void * privload_tls_init(void *app_tls) { void *res; /* We have to exactly duplicate the offset of key fields in Android's * pthread_internal_t struct. */ ASSERT(PTHREAD_TLS_OFFS == offsetof(android_pthread_internal_t, tls)); ASSERT(DR_TLS_BASE_OFFSET == offsetof(android_pthread_internal_t, dr_tls_base) - PTHREAD_TLS_OFFS /* the self slot */); if (!dynamo_initialized) { char **e; /* We have to duplicate the pthread setup that the Android loader does. * We expect app_tls to be either NULL or garbage, as we have early injection. */ init_thread.tid = dynamorio_syscall(SYS_set_tid_address, 1, &init_thread.tid); init_thread.cached_pid_ = init_thread.tid; /* init_thread.attr is set to all 0 (sched is SCHED_NORMAL==0, and sizes are * zeroed out) */ /* init_thread.join_state is set to 0 (THREAD_NOT_JOINED) */ init_thread.tls[ANDROID_TLS_SLOT_SELF] = init_thread.tls; init_thread.tls[ANDROID_TLS_SLOT_THREAD_ID] = &init_thread; /* tls[TLS_SLOT_STACK_GUARD] is set to 0 */ /* Set up the data struct pointing at kernel args that Bionic expects */ kernel_args.argc = *(int *)kernel_init_sp; kernel_args.argv = (char **)kernel_init_sp + 1; kernel_args.envp = kernel_args.argv + kernel_args.argc + 1; /* The aux vector is after the last environment pointer. */ for (e = kernel_args.envp; *e != NULL; e++) ; /* nothing */ kernel_args.auxv = (ELF_AUXV_TYPE *)(e + 1); init_thread.tls[ANDROID_TLS_SLOT_BIONIC_PREINIT] = &kernel_args; /* We use our own alternate signal stack */ LOG(GLOBAL, LOG_LOADER, 2, "%s: kernel sp is "PFX"; TLS set to "PFX"\n", __FUNCTION__, init_thread.tls[ANDROID_TLS_SLOT_BIONIC_PREINIT], init_thread.tls[ANDROID_TLS_SLOT_SELF]); res = init_thread.tls[ANDROID_TLS_SLOT_SELF]; } else { android_pthread_internal_t *thrd; res = heap_mmap(ALIGN_FORWARD(sizeof(android_pthread_internal_t), PAGE_SIZE)); LOG(GLOBAL, LOG_LOADER, 2, "%s: allocated new TLS at "PFX"; copying from "PFX"\n", __FUNCTION__, res, app_tls); if (app_tls != NULL) memcpy(res, app_tls, sizeof(android_pthread_internal_t)); thrd = (android_pthread_internal_t *) res; thrd->tls[ANDROID_TLS_SLOT_SELF] = thrd->tls; thrd->tls[ANDROID_TLS_SLOT_THREAD_ID] = thrd; thrd->tid = get_thread_id(); thrd->dr_tls_base = NULL; res = thrd->tls[ANDROID_TLS_SLOT_SELF]; LOG(GLOBAL, LOG_LOADER, 2, "%s: TLS set to "PFX"\n", __FUNCTION__, thrd->tls[ANDROID_TLS_SLOT_SELF]); } /* Android does not yet support per-module TLS */ return res; }
/* callback for dl_iterate_phdr() for adding existing modules to our lists */ static int dl_iterate_get_areas_cb(struct dl_phdr_info *info, size_t size, void *data) { int *count = (int *)data; uint i; /* see comments in dl_iterate_get_path_cb() */ app_pc modend; app_pc min_vaddr = module_vaddr_from_prog_header((app_pc)info->dlpi_phdr, info->dlpi_phnum, NULL, &modend); app_pc modbase = info->dlpi_addr + min_vaddr; size_t modsize = modend - min_vaddr; LOG(GLOBAL, LOG_VMAREAS, 2, "dl_iterate_get_areas_cb: addr=" PFX " hdrs=" PFX " base=" PFX " name=%s\n", info->dlpi_addr, info->dlpi_phdr, modbase, info->dlpi_name); ASSERT(info->dlpi_phnum == module_num_program_headers(modbase)); ASSERT(count != NULL); if (*count == 0) { /* since we don't get a name for the executable, for now we * assume that the first iter is the executable itself. * XXX: this seems to hold, but there's no guarantee: can we do better? */ executable_start = modbase; } #ifndef X64 if (modsize == PAGE_SIZE && info->dlpi_name[0] == '\0') { /* Candidate for VDSO. Xref PR 289138 on using AT_SYSINFO to locate. */ /* Xref VSYSCALL_PAGE_START_HARDCODED but later linuxes randomize */ char *soname; if (module_walk_program_headers(modbase, modsize, false, true, /* i#1589: ld.so relocated .dynamic */ NULL, NULL, NULL, &soname, NULL) && strncmp(soname, VSYSCALL_PAGE_SO_NAME, strlen(VSYSCALL_PAGE_SO_NAME)) == 0) { ASSERT(!dynamo_initialized); /* .data should be +w */ ASSERT(vsyscall_page_start == NULL); vsyscall_page_start = modbase; LOG(GLOBAL, LOG_VMAREAS, 1, "found vsyscall page @ " PFX "\n", vsyscall_page_start); } } #endif if (modbase != vsyscall_page_start) module_list_add(modbase, modsize, false, info->dlpi_name, 0 /*don't have inode*/); for (i = 0; i < info->dlpi_phnum; i++) { app_pc start, end; uint prot; size_t align; if (module_read_program_header(modbase, i, &start, &end, &prot, &align)) { start += info->dlpi_addr; end += info->dlpi_addr; LOG(GLOBAL, LOG_VMAREAS, 2, "\tsegment %d: " PFX "-" PFX " %s align=%d\n", i, start, end, memprot_string(prot), align); start = (app_pc)ALIGN_BACKWARD(start, PAGE_SIZE); end = (app_pc)ALIGN_FORWARD(end, PAGE_SIZE); LOG(GLOBAL, LOG_VMAREAS, 4, "find_executable_vm_areas: adding: " PFX "-" PFX " prot=%d\n", start, end, prot); all_memory_areas_lock(); update_all_memory_areas(start, end, prot, DR_MEMTYPE_IMAGE); all_memory_areas_unlock(); if (app_memory_allocation(NULL, start, end - start, prot, true /*image*/ _IF_DEBUG("ELF SO"))) (*count)++; } } return 0; /* keep iterating */ }
// init memory layout // This function should perform init of "memory layout object" and // init the primary guest memory layout. // In the case of no secondary guests, memory layout object is not required // // For primary guest: // - All memory upto 4G is mapped, except for VMM and secondary guest areas // - Only specified memory above 4G is mapped. Mapping in the >4G region for primary // guest should be added by demand // // For secondary guests: // - All secondary guests are loaded lower than 4G BOOLEAN init_memory_layout_from_mbr( #if 0 // JLM(FIX) int num_excluded #endif const VMM_MEMORY_LAYOUT* vmm_memory_layout, GPM_HANDLE primary_guest_gpm, BOOLEAN are_secondary_guests_exist, const VMM_APPLICATION_PARAMS_STRUCT* application_params) { E820_ABSTRACTION_RANGE_ITERATOR e820_iter; const INT15_E820_MEMORY_MAP_ENTRY_EXT* e820_entry = NULL; BOOLEAN ok; UINT64 range_start; UINT64 range_end; INT15_E820_RANGE_TYPE range_type; INT15_E820_MEMORY_MAP_EXT_ATTRIBUTES range_attr; UINT64 page_index; UINT64 *entry_list; // BEFORE_VMLAUNCH. CRITICAL check that should not fail. VMM_ASSERT(e820_abstraction_is_initialized()); if (global_policy_uses_vtlb()) { mam_rwx_attrs.uint32 = 0x5; mam_rw_attrs.uint32 = 0x1; mam_ro_attrs.uint32= 0x0; } // 1. first map 0-4G host region to primary guest ok = gpm_add_mapping( primary_guest_gpm, 0, 0, FOUR_GIGABYTE, mam_rwx_attrs ); VMM_LOG(mask_anonymous, level_trace,"Primary guest GPM: add 0-4G region\r\n"); // BEFORE_VMLAUNCH. CRITICAL check that should not fail. VMM_ASSERT( ok == TRUE ); // 2. Add real memory to "memory layout object" and to the primary guest // if this memory range is above 4G // if in the post launch mode skip it for (e820_iter = e820_abstraction_iterator_get_first(E820_ORIGINAL_MAP); e820_iter != E820_ABSTRACTION_NULL_ITERATOR; e820_iter = e820_abstraction_iterator_get_next(E820_ORIGINAL_MAP, e820_iter)) { e820_entry = e820_abstraction_iterator_get_range_details(e820_iter); range_start = e820_entry->basic_entry.base_address; range_end = range_start + e820_entry->basic_entry.length; range_type = e820_entry->basic_entry.address_range_type; range_attr = e820_entry->extended_attributes; // align ranges and sizes on 4K boundaries range_start = ALIGN_FORWARD(range_start, PAGE_4KB_SIZE); range_end = ALIGN_BACKWARD(range_end, PAGE_4KB_SIZE); VMM_DEBUG_CODE({ if (range_start != e820_entry->basic_entry.base_address) { VMM_LOG(mask_anonymous, level_trace,"init_memory_layout_from_mbr WARNING: aligning E820 range start from %P to %P\n", e820_entry->basic_entry.base_address, range_start); } if (range_end != e820_entry->basic_entry.base_address + e820_entry->basic_entry.length) { VMM_LOG(mask_anonymous, level_trace,"init_memory_layout_from_mbr WARNING: aligning E820 range end from %P to %P\n", e820_entry->basic_entry.base_address+e820_entry->basic_entry.length, range_end); } }) if (range_end <= range_start) { // after alignment the range became invalid VMM_LOG(mask_anonymous, level_trace,"init_memory_layout_from_mbr WARNING: skipping invalid E820 memory range FROM %P to %P\n", range_start, range_end); continue; } // add memory to the "memory layout object" if this is a real memory // lower 4G if (are_secondary_guests_exist && (range_start < FOUR_GIGABYTE) && range_attr.Bits.enabled && (!range_attr.Bits.non_volatile)) { UINT64 top = (range_end < FOUR_GIGABYTE) ? range_end : FOUR_GIGABYTE; (void)top; if ((range_type == INT15_E820_ADDRESS_RANGE_TYPE_MEMORY) || (range_type == INT15_E820_ADDRESS_RANGE_TYPE_ACPI)) { // here we need to all a call to the "memory layout object" // to fill is with the range_start-top range // to make compiler happy top = 0; } } // add memory to the primary guest if this is a memory above 4G if (range_end > FOUR_GIGABYTE) { UINT64 bottom = (range_start < FOUR_GIGABYTE) ? FOUR_GIGABYTE : range_start; if (bottom < range_end) { VMM_LOG(mask_anonymous, level_trace,"Primary guest GPM: add memory above 4GB base %p size %p\r\n", bottom, range_end - bottom); ok = gpm_add_mapping( primary_guest_gpm, bottom, bottom, range_end - bottom, mam_rwx_attrs ); // BEFORE_VMLAUNCH. CRITICAL check that should not fail. VMM_ASSERT( ok == TRUE ); } } }