aboutsummaryrefslogtreecommitdiffstats
path: root/memcheck/memcheck_util.c
diff options
context:
space:
mode:
authorVladimir Chtchetkine <vchtchetkine@google.com>2010-02-16 10:38:35 -0800
committerVladimir Chtchetkine <vchtchetkine@google.com>2010-02-18 15:22:07 -0800
commit5389aa19033153c09556d1362a8b8a56abccb8f5 (patch)
tree5d731effe5bd5d2f162f06aadec7212045eaef3d /memcheck/memcheck_util.c
parent76dbca0489ab98a46f2954bc7b77c3df6f9d8264 (diff)
downloadexternal_qemu-5389aa19033153c09556d1362a8b8a56abccb8f5.zip
external_qemu-5389aa19033153c09556d1362a8b8a56abccb8f5.tar.gz
external_qemu-5389aa19033153c09556d1362a8b8a56abccb8f5.tar.bz2
Merge memory checking from sandbox
Change-id: Ibce845d0
Diffstat (limited to 'memcheck/memcheck_util.c')
-rw-r--r--memcheck/memcheck_util.c270
1 files changed, 270 insertions, 0 deletions
diff --git a/memcheck/memcheck_util.c b/memcheck/memcheck_util.c
new file mode 100644
index 0000000..cc4182d
--- /dev/null
+++ b/memcheck/memcheck_util.c
@@ -0,0 +1,270 @@
+/* Copyright (C) 2007-2010 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program 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 for more details.
+*/
+
+/*
+ * Contains implementation of utility routines for memchecker framework.
+ */
+
+/* This file should compile iff qemu is built with memory checking
+ * configuration turned on. */
+#ifndef CONFIG_MEMCHECK
+#error CONFIG_MEMCHECK is not defined.
+#endif // CONFIG_MEMCHECK
+
+#include "stdio.h"
+#include "qemu-common.h"
+#include "android/utils/path.h"
+#include "cpu.h"
+#include "softmmu_outside_jit.h"
+#include "memcheck_proc_management.h"
+#include "memcheck_logging.h"
+#include "memcheck_util.h"
+
+/* Gets symblos file path for the given module.
+ * Param:
+ * module_path - Path to the module to get sympath for.
+ * sym_path - Buffer, where to save path to the symbols file path for the givem
+ * module. NOTE: This buffer must be big enough to contain the largest
+ * path possible.
+ * max_char - Character size of the buffer addressed by sym_path parameter.
+ * Return:
+ * 0 on success, or -1 if symbols file has not been found, or sym_path buffer
+ * was too small to contain entire path.
+ */
+static int
+get_sym_path(const char* module_path, char* sym_path, size_t max_char)
+{
+ const char* sym_path_root = getenv("ANDROID_PROJECT_OUT");
+ if (sym_path_root == NULL || strlen(sym_path_root) >= max_char) {
+ return -1;
+ }
+
+ strcpy(sym_path, sym_path_root);
+ max_char -= strlen(sym_path_root);
+ if (sym_path[strlen(sym_path)-1] != PATH_SEP_C) {
+ strcat(sym_path, PATH_SEP);
+ max_char--;
+ }
+ if (strlen("symbols") >= max_char) {
+ return -1;
+ }
+ strcat(sym_path, "symbols");
+ max_char -= strlen("symbols");
+ if (strlen(module_path) >= max_char) {
+ return -1;
+ }
+ strcat(sym_path, module_path);
+
+ /* Sometimes symbol file for a module is placed into a parent symbols
+ * directory. Lets iterate through all parent sym dirs, until we find
+ * sym file, or reached symbols root. */
+ while (!path_exists(sym_path)) {
+ /* Select module name. */
+ char* name = strrchr(sym_path, PATH_SEP_C);
+ assert(name != NULL);
+ *name = '\0';
+ /* Parent directory. */
+ char* parent = strrchr(sym_path, PATH_SEP_C);
+ assert(parent != NULL);
+ *parent = '\0';
+ if (strcmp(sym_path, sym_path_root) == 0) {
+ return -1;
+ }
+ *parent = PATH_SEP_C;
+ memmove(parent+1, name + 1, strlen(name + 1) + 1);
+ }
+
+ return 0;
+}
+
+// =============================================================================
+// Transfering data between guest and emulator address spaces.
+// =============================================================================
+
+void
+memcheck_get_guest_buffer(void* qemu_address,
+ target_ulong guest_address,
+ size_t buffer_size)
+{
+ /* Byte-by-byte copying back and forth between guest's and emulator's memory
+ * appears to be efficient enough (at least on small blocks used in
+ * memchecker), so there is no real need to optimize it by aligning guest
+ * buffer to 32 bits and use ld/stl_user instead of ld/stub_user to
+ * read / write guest's memory. */
+ while (buffer_size) {
+ *(uint8_t*)qemu_address = ldub_user(guest_address);
+ (uint32_t)qemu_address++;
+ guest_address++;
+ buffer_size--;
+ }
+}
+
+void
+memcheck_set_guest_buffer(target_ulong guest_address,
+ const void* qemu_address,
+ size_t buffer_size)
+{
+ while (buffer_size) {
+ stb_user(guest_address, *(uint8_t*)qemu_address);
+ guest_address++;
+ (uint32_t)qemu_address++;
+ buffer_size--;
+ }
+}
+
+size_t
+memcheck_get_guest_string(char* qemu_str,
+ target_ulong guest_str,
+ size_t qemu_buffer_size)
+{
+ size_t copied = 0;
+
+ if (qemu_buffer_size > 1) {
+ for (copied = 0; copied < qemu_buffer_size - 1; copied++) {
+ qemu_str[copied] = ldub_user(guest_str + copied);
+ if (qemu_str[copied] == '\0') {
+ return copied;
+ }
+ }
+ }
+ qemu_str[copied] = '\0';
+ return copied;
+}
+
+size_t
+memcheck_get_guest_kernel_string(char* qemu_str,
+ target_ulong guest_str,
+ size_t qemu_buffer_size)
+{
+ size_t copied = 0;
+
+ if (qemu_buffer_size > 1) {
+ for (copied = 0; copied < qemu_buffer_size - 1; copied++) {
+ qemu_str[copied] = ldub_kernel(guest_str + copied);
+ if (qemu_str[copied] == '\0') {
+ return copied;
+ }
+ }
+ }
+ qemu_str[copied] = '\0';
+ return copied;
+}
+
+// =============================================================================
+// Helpers for transfering memory allocation information.
+// =============================================================================
+
+void
+memcheck_fail_alloc(target_ulong guest_address)
+{
+ stl_user(ALLOC_RES_ADDRESS(guest_address), 0);
+}
+
+void
+memcheck_fail_free(target_ulong guest_address)
+{
+ stl_user(FREE_RES_ADDRESS(guest_address), 0);
+}
+
+void
+memcheck_fail_query(target_ulong guest_address)
+{
+ stl_user(QUERY_RES_ADDRESS(guest_address), 0);
+}
+
+// =============================================================================
+// Misc. utility routines.
+// =============================================================================
+
+void
+invalidate_tlb_cache(target_ulong start, target_ulong end)
+{
+ target_ulong index = (start >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ const target_ulong to = ((end - 1) >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE-1);
+ for (; index <= to; index++, start += TARGET_PAGE_SIZE) {
+ target_ulong tlb_addr = cpu_single_env->tlb_table[1][index].addr_write;
+ if ((start & TARGET_PAGE_MASK) ==
+ (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ cpu_single_env->tlb_table[1][index].addr_write ^= TARGET_PAGE_MASK;
+ }
+ tlb_addr = cpu_single_env->tlb_table[1][index].addr_read;
+ if ((start & TARGET_PAGE_MASK) ==
+ (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ cpu_single_env->tlb_table[1][index].addr_read ^= TARGET_PAGE_MASK;
+ }
+ }
+}
+
+void
+memcheck_dump_malloc_desc(const MallocDescEx* desc_ex,
+ int print_flags,
+ int print_proc_info)
+{
+ const MallocDesc* desc = &desc_ex->malloc_desc;
+ printf(" User range: 0x%08X - 0x%08X, %u bytes\n",
+ (uint32_t)mallocdesc_get_user_ptr(desc),
+ (uint32_t)mallocdesc_get_user_ptr(desc) + desc->requested_bytes,
+ desc->requested_bytes);
+ printf(" Prefix guarding area: 0x%08X - 0x%08X, %u bytes\n",
+ desc->ptr, desc->ptr + desc->prefix_size, desc->prefix_size);
+ printf(" Suffix guarding area: 0x%08X - 0x%08X, %u bytes\n",
+ mallocdesc_get_user_alloc_end(desc),
+ mallocdesc_get_user_alloc_end(desc) + desc->suffix_size,
+ desc->suffix_size);
+ if (print_proc_info) {
+ ProcDesc* proc = get_process_from_pid(desc->allocator_pid);
+ if (proc != NULL) {
+ printf(" Allocated by: %s[pid=%u]\n",
+ proc->image_path, proc->pid);
+ }
+ }
+ if (print_flags) {
+ printf(" Flags: 0x%08X\n", desc_ex->flags);
+ }
+}
+
+int
+memcheck_get_address_info(target_ulong abs_pc,
+ const MMRangeDesc* rdesc,
+ Elf_AddressInfo* info,
+ ELFF_HANDLE* elff_handle)
+{
+ char sym_path[MAX_PATH];
+ ELFF_HANDLE handle;
+
+ if (get_sym_path(rdesc->path, sym_path, MAX_PATH)) {
+ return 1;
+ }
+
+ handle = elff_init(sym_path);
+ if (handle == NULL) {
+ return -1;
+ }
+
+ if (!elff_is_exec(handle)) {
+ /* Debug info for shared library is created for the relative address. */
+ target_ulong rel_pc = mmrangedesc_get_module_offset(rdesc, abs_pc);
+ if (elff_get_pc_address_info(handle, rel_pc, info)) {
+ elff_close(handle);
+ return -1;
+ }
+ } else {
+ /* Debug info for executables is created for the absoulte address. */
+ if (elff_get_pc_address_info(handle, abs_pc, info)) {
+ elff_close(handle);
+ return -1;
+ }
+ }
+
+ *elff_handle = handle;
+ return 0;
+}