Merge memory checking from sandbox

Change-id: Ibce845d0
diff --git a/memcheck/memcheck.c b/memcheck/memcheck.c
new file mode 100644
index 0000000..9308c9f
--- /dev/null
+++ b/memcheck/memcheck.c
@@ -0,0 +1,648 @@
+/* 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 memory checking framework in the emulator.
+ */
+
+/* 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 "sys-queue.h"
+#include "qemu_file.h"
+#include "elff_api.h"
+#include "memcheck.h"
+#include "memcheck_proc_management.h"
+#include "memcheck_util.h"
+#include "memcheck_logging.h"
+
+// =============================================================================
+// Global data
+// =============================================================================
+
+/* Controls what messages from the guest should be printed to emulator's
+ * stdout. This variable holds a combinations of TRACE_LIBC_XXX flags. */
+uint32_t trace_flags = 0;
+
+/* Global flag, indicating whether or not memchecking has been enabled
+ * for the current emulator session. 1 means that memchecking has been enabled,
+ * 0 means that memchecking has not been enabled. */
+int memcheck_enabled = 0;
+
+/* Global flag, indicating whether or not __ld/__stx_mmu should be instrumented
+ * for checking for access violations. If read / write access violation check
+ * has been disabled by -memcheck flags, there is no need to instrument mmu
+ * routines and waste performance.
+ * 1 means that instrumenting is required, 0 means that instrumenting is not
+ * required. */
+int memcheck_instrument_mmu = 0;
+
+/* Global flag, indicating whether or not memchecker is collecting call stack.
+ * 1 - call stack is being collected, 0 means that stack is not being
+ * collected. */
+int memcheck_watch_call_stack = 1;
+
+// =============================================================================
+// Static routines.
+// =============================================================================
+
+/* Prints invalid pointer access violation information.
+ * Param:
+ *  proc - Process that caused access violation.
+ *  ptr - Pointer that caused access violation.
+ *  routine - If 1, access violation has occurred in 'free' routine.
+ *      If 2, access violation has occurred in 'realloc' routine.
+ */
+static void
+av_invalid_pointer(ProcDesc* proc, target_ulong ptr, int routine)
+{
+    if (trace_flags & TRACE_CHECK_INVALID_PTR_ENABLED) {
+        printf("memcheck: Access violation is detected in process %s[pid=%u]:\n"
+          "  INVALID POINTER 0x%08X is used in '%s' operation.\n"
+          "  Allocation descriptor for this pointer has not been found in the\n"
+          "  allocation map for the process. Most likely, this is an attempt\n"
+          "  to %s a pointer that has been freed.\n",
+          proc->image_path, proc->pid, ptr, routine == 1 ? "free" : "realloc",
+          routine == 1 ? "free" : "reallocate");
+    }
+}
+
+/* Prints read / write access violation information.
+ * Param:
+ *  proc - Process that caused access violation.
+ *  desc - Allocation descriptor for the violation.
+ *  addr - Address at which vilation has occurred.
+ *  data_size - Size of data accessed at the 'addr'.
+ *  val - If access violation has occurred at write operation, this parameter
+ *      contains value that's being written to 'addr'. For read violation this
+ *      parameter is not used.
+ *  retaddr - Code address (in TB) where access violation has occurred.
+ *  is_read - If 1, access violation has occurred when memory at 'addr' has been
+ *      read. If 0, access violation has occurred when memory was written.
+ */
+static void
+av_access_violation(ProcDesc* proc,
+                    MallocDescEx* desc,
+                    target_ulong addr,
+                    uint32_t data_size,
+                    uint64_t val,
+                    target_ulong retaddr,
+                    int is_read)
+{
+    target_ulong vaddr;
+    Elf_AddressInfo elff_info;
+    ELFF_HANDLE elff_handle = NULL;
+
+    desc->malloc_desc.av_count++;
+    if ((is_read && !(trace_flags & TRACE_CHECK_READ_VIOLATION_ENABLED)) ||
+        (!is_read && !(trace_flags & TRACE_CHECK_WRITE_VIOLATION_ENABLED))) {
+        return;
+    }
+
+    /* Convert host address to guest address. */
+    vaddr = memcheck_tpc_to_gpc(retaddr);
+    printf("memcheck: Access violation is detected in process %s[pid=%u]:\n",
+           proc->image_path, proc->pid);
+
+    /* Obtain routine, filename / line info for the address. */
+    const MMRangeDesc* rdesc = procdesc_get_range_desc(proc, vaddr);
+    if (rdesc != NULL) {
+        int elff_res;
+        printf("  In module %s at address 0x%08X\n", rdesc->path, vaddr);
+        elff_res =
+          memcheck_get_address_info(vaddr, rdesc, &elff_info, &elff_handle);
+        if (elff_res == 0) {
+            printf("  In routine %s in %s/%s:%u\n",
+                   elff_info.routine_name, elff_info.dir_name,
+                   elff_info.file_name, elff_info.line_number);
+            if (elff_info.inline_stack != NULL) {
+                const Elf_InlineInfo* inl = elff_info.inline_stack;
+                int index = 0;
+                for (; inl[index].routine_name != NULL; index++) {
+                    char align[64];
+                    size_t set_align = 4 + index * 2;
+                    if (set_align >= sizeof(align)) {
+                        set_align = sizeof(align) -1;
+                    }
+                    memset(align, ' ', set_align);
+                    align[set_align] = '\0';
+                    printf(align);
+                    if (inl[index].inlined_in_file == NULL) {
+                        printf("inlined to %s in unknown location\n",
+                               inl[index].routine_name);
+                    } else {
+                        printf("inlined to %s in %s/%s:%u\n",
+                               inl[index].routine_name,
+                               inl[index].inlined_in_file_dir,
+                               inl[index].inlined_in_file,
+                               inl[index].inlined_at_line);
+                    }
+                }
+            }
+            elff_free_pc_address_info(elff_handle, &elff_info);
+            elff_close(elff_handle);
+        } else if (elff_res == 1) {
+            printf("  Unable to obtain routine information. Symbols file is not found.\n");
+        } else {
+            printf("  Unable to obtain routine information.\n"
+                   "  Symbols file doesn't contain debugging information for address 0x%08X.\n",
+                    mmrangedesc_get_module_offset(rdesc, vaddr));
+        }
+    } else {
+        printf("  In unknown module at address 0x%08X\n", vaddr);
+    }
+
+    printf("  Process attempts to %s %u bytes %s address 0x%08X\n",
+           is_read ? "read" : "write", data_size,
+           is_read ? "from" : "to", addr);
+    printf("  Accessed range belongs to the %s guarding area of allocated block.\n",
+           addr < (target_ulong)mallocdesc_get_user_ptr(&desc->malloc_desc) ?
+                "prefix" : "suffix");
+    printf("  Allocation descriptor for this violation:\n");
+    memcheck_dump_malloc_desc(desc, 1, 0);
+}
+
+/* Validates access to a guest address.
+ * Param:
+ *  addr - Virtual address in the guest space where memory is accessed.
+ *  data_size - Size of the accessed data.
+ *  proc_ptr - Upon exit from this routine contains pointer to the process
+ *      descriptor for the current process, or NULL, if no such descriptor has
+ *      been found.
+ *  desc_ptr - Upon exit from this routine contains pointer to the allocation
+ *      descriptor matching given address range, or NULL, if allocation
+ *      descriptor for the validated memory range has not been found.
+ * Return:
+ *  0 if access to the given guest address range doesn't violate anything, or
+ *  1 if given guest address range doesn't match any entry in the current
+ *      process allocation descriptors map, or
+ *  -1 if a violation has been detected.
+ */
+static int
+memcheck_common_access_validation(target_ulong addr,
+                                  uint32_t data_size,
+                                  ProcDesc** proc_ptr,
+                                  MallocDescEx** desc_ptr)
+{
+    MallocDescEx* desc;
+    target_ulong validating_range_end;
+    target_ulong user_range_end;
+
+    ProcDesc* proc = get_current_process();
+    *proc_ptr = proc;
+    if (proc == NULL) {
+        *desc_ptr = NULL;
+        return 1;
+    }
+
+    desc = procdesc_find_malloc_for_range(proc, addr, data_size);
+    *desc_ptr = desc;
+    if (desc == NULL) {
+        return 1;
+    }
+
+    /* Verify that validating address range doesn't start before the address
+     * available to the user. */
+    if (addr < mallocdesc_get_user_ptr(&desc->malloc_desc)) {
+        // Stepped on the prefix guarding area.
+        return -1;
+    }
+
+    validating_range_end = addr + data_size;
+    user_range_end = mallocdesc_get_user_alloc_end(&desc->malloc_desc);
+
+    /* Verify that validating address range ends inside the user block.
+     * We may step on the suffix guarding area because of alignment issue.
+     * For example, the application code reads last byte in the allocated block
+     * with something like this:
+     *
+     *      char last_byte_value = *(char*)last_byte_address;
+     *
+     * and this code got compiled into something like this:
+     *
+     *      mov eax, [last_byte_address];
+     *      mov [last_byte_value], al;
+     *
+     * In this case we will catch a read from the suffix area, even though
+     * there were no errors in the code. So, in order to prevent such "false
+     * negative" alarms, lets "forgive" this violation.
+     * There is one bad thing about this "forgivness" though, as it may very
+     * well be, that in real life some of these "out of bound" bytes will cross
+     * page boundaries, marching into a page that has not been mapped to the
+     * process.
+     */
+    if (validating_range_end <= user_range_end) {
+        // Validating address range is fully contained inside the user block.
+        return 0;
+    }
+
+    /* Lets see if this AV is caused by an alignment issue.*/
+    if ((validating_range_end - user_range_end) < data_size) {
+        /* Could be an alignment. */
+        return 0;
+    }
+
+    return -1;
+}
+
+/* Checks if process has allocation descriptors for pages defined by a buffer.
+ * Param:
+ *  addr - Starting address of a buffer.
+ *  buf_size - Buffer size.
+ * Return:
+ *  1 if process has allocations descriptors for pages defined by a buffer, or
+ *  0 if pages containing given buffer don't have any memory allocations in
+ *  them.
+ */
+static inline int
+procdesc_contains_allocs(ProcDesc* proc, target_ulong addr, uint32_t buf_size) {
+    if (proc != NULL) {
+        // Beginning of the page containing last byte in range.
+        const target_ulong end_page = (addr + buf_size - 1) & TARGET_PAGE_MASK;
+        // Adjust beginning of the range to the beginning of the page.
+        addr &= TARGET_PAGE_MASK;
+        // Total size of range to check for descriptors.
+        buf_size = end_page - addr + TARGET_PAGE_SIZE + 1;
+        return procdesc_find_malloc_for_range(proc, addr, buf_size) ? 1 : 0;
+    } else {
+        return 0;
+    }
+}
+
+// =============================================================================
+// Memchecker API.
+// =============================================================================
+
+void
+memcheck_init(const char* tracing_flags)
+{
+    if (*tracing_flags == '0') {
+        // Memchecker is disabled.
+        return;
+    } else if (*tracing_flags == '1') {
+        // Set default tracing.
+        trace_flags = TRACE_CHECK_LEAK_ENABLED             |
+                      TRACE_CHECK_READ_VIOLATION_ENABLED   |
+                      TRACE_CHECK_INVALID_PTR_ENABLED      |
+                      TRACE_CHECK_WRITE_VIOLATION_ENABLED;
+    }
+
+    // Parse -memcheck option params, converting them into tracing flags.
+    while (*tracing_flags) {
+        switch (*tracing_flags) {
+            case 'A':
+                // Enable all emulator's tracing messages.
+                trace_flags |= TRACE_ALL_ENABLED;
+                break;
+            case 'F':
+                // Enable fork() tracing.
+                trace_flags |= TRACE_PROC_FORK_ENABLED;
+                break;
+            case 'S':
+                // Enable guest process staring tracing.
+                trace_flags |= TRACE_PROC_START_ENABLED;
+                break;
+            case 'E':
+                // Enable guest process exiting tracing.
+                trace_flags |= TRACE_PROC_EXIT_ENABLED;
+                break;
+            case 'C':
+                // Enable clone() tracing.
+                trace_flags |= TRACE_PROC_CLONE_ENABLED;
+                break;
+            case 'N':
+                // Enable new PID allocation tracing.
+                trace_flags |= TRACE_PROC_NEW_PID_ENABLED;
+                break;
+            case 'B':
+                // Enable libc.so initialization tracing.
+                trace_flags |= TRACE_PROC_LIBC_INIT_ENABLED;
+                break;
+            case 'L':
+                // Enable memory leaks tracing.
+                trace_flags |= TRACE_CHECK_LEAK_ENABLED;
+                break;
+            case 'I':
+                // Enable invalid free / realloc pointer tracing.
+                trace_flags |= TRACE_CHECK_INVALID_PTR_ENABLED;
+                break;
+            case 'R':
+                // Enable reading violations tracing.
+                trace_flags |= TRACE_CHECK_READ_VIOLATION_ENABLED;
+                break;
+            case 'W':
+                // Enable writing violations tracing.
+                trace_flags |= TRACE_CHECK_WRITE_VIOLATION_ENABLED;
+                break;
+            case 'M':
+                // Enable module mapping tracing.
+                trace_flags |= TRACE_PROC_MMAP_ENABLED;
+                break;
+            default:
+                break;
+        }
+        if (trace_flags == TRACE_ALL_ENABLED) {
+            break;
+        }
+        tracing_flags++;
+    }
+
+    /* Lets see if we need to instrument MMU, injecting memory access checking.
+     * We instrument MMU only if we monitor read, or write memory access. */
+    if (trace_flags & (TRACE_CHECK_READ_VIOLATION_ENABLED |
+                       TRACE_CHECK_WRITE_VIOLATION_ENABLED)) {
+        memcheck_instrument_mmu = 1;
+    } else {
+        memcheck_instrument_mmu = 0;
+    }
+
+    memcheck_init_proc_management();
+
+    /* Lets check env. variables needed for memory checking. */
+    if (getenv("ANDROID_PROJECT_OUT") == NULL) {
+        printf("memcheck: Missing ANDROID_PROJECT_OUT environment variable, that is used\n"
+               "to calculate path to symbol files.\n");
+    }
+
+    // Always set this flag at the very end of the initialization!
+    memcheck_enabled = 1;
+}
+
+void
+memcheck_guest_libc_initialized(uint32_t pid)
+{
+    ProcDesc* proc = get_process_from_pid(pid);
+    if (proc == NULL) {
+        ME("memcheck: Unable to obtain process for libc_init pid=%u", pid);
+        return;
+    }
+    proc->flags |= PROC_FLAG_LIBC_INITIALIZED;
+
+    /* When process initializes its own libc.so instance, it means that now
+     * it has fresh heap. So, at this point we must get rid of all entries
+     * (inherited and transition) that were collected in this process'
+     * allocation descriptors map. */
+    procdesc_empty_alloc_map(proc);
+    T(PROC_LIBC_INIT, "memcheck: libc.so has been initialized for %s[pid=%u]\n",
+      proc->image_path, proc->pid);
+}
+
+void
+memcheck_guest_alloc(target_ulong guest_address)
+{
+    MallocDescEx desc;
+    MallocDescEx replaced;
+    RBTMapResult insert_res;
+    ProcDesc* proc;
+    ThreadDesc* thread;
+    uint32_t indx;
+
+    // Copy allocation descriptor from guest to emulator.
+    memcheck_get_malloc_descriptor(&desc.malloc_desc, guest_address);
+    desc.flags = 0;
+    desc.call_stack = NULL;
+    desc.call_stack_count = 0;
+
+    proc = get_process_from_pid(desc.malloc_desc.allocator_pid);
+    if (proc == NULL) {
+        ME("memcheck: Unable to obtain process for allocation pid=%u",
+           desc.malloc_desc.allocator_pid);
+        memcheck_fail_alloc(guest_address);
+        return;
+    }
+
+    if (!procdesc_is_executing(proc)) {
+        desc.flags |= MDESC_FLAG_TRANSITION_ENTRY;
+    }
+
+    /* Copy thread's calling stack to the allocation descriptor. */
+    thread = get_current_thread();
+    desc.call_stack_count = thread->call_stack_count;
+    if (desc.call_stack_count) {
+        desc.call_stack = qemu_malloc(desc.call_stack_count * sizeof(target_ulong));
+        if (desc.call_stack == NULL) {
+            ME("memcheck: Unable to allocate %u bytes for the calling stack",
+               desc.call_stack_count * sizeof(target_ulong));
+            return;
+        }
+    }
+
+    /* Thread's calling stack is in descending order (i.e. first entry in the
+     * thread's stack is the most distant routine from the current one). On the
+     * other hand, we keep calling stack entries in allocation descriptor in
+     * assending order. */
+    for (indx = 0; indx < thread->call_stack_count; indx++) {
+        desc.call_stack[indx] =
+           thread->call_stack[thread->call_stack_count - 1 - indx].call_address;
+    }
+
+    // Save malloc descriptor in the map.
+    insert_res = procdesc_add_malloc(proc, &desc, &replaced);
+    if (insert_res == RBT_MAP_RESULT_ENTRY_INSERTED) {
+        // Invalidate TLB cache for the allocated block.
+        if (memcheck_instrument_mmu) {
+            invalidate_tlb_cache(desc.malloc_desc.ptr,
+                                mallocdesc_get_alloc_end(&desc.malloc_desc));
+        }
+    } else if (insert_res == RBT_MAP_RESULT_ENTRY_REPLACED) {
+        /* We don't expect to have another entry in the map that matches
+         * inserting entry. This is an error condition for us, indicating
+         * that we somehow lost track of memory allocations. */
+        ME("memcheck: Duplicate allocation blocks:");
+        if (VERBOSE_CHECK(memcheck)) {
+            printf("   New block:\n");
+            memcheck_dump_malloc_desc(&desc, 1, 1);
+            printf("   Replaced block:\n");
+            memcheck_dump_malloc_desc(&replaced, 1, 1);
+        }
+        if (replaced.call_stack != NULL) {
+            qemu_free(replaced.call_stack);
+        }
+    } else {
+        ME("memcheck: Unable to insert an entry to the allocation map:");
+        if (VERBOSE_CHECK(memcheck)) {
+            memcheck_dump_malloc_desc(&desc, 1, 1);
+        }
+        memcheck_fail_alloc(guest_address);
+        return;
+    }
+}
+
+void
+memcheck_guest_free(target_ulong guest_address)
+{
+    MallocFree desc;
+    MallocDescEx pulled;
+    int pull_res;
+    ProcDesc* proc;
+
+    // Copy free descriptor from guest to emulator.
+    memcheck_get_free_descriptor(&desc, guest_address);
+
+    proc = get_process_from_pid(desc.free_pid);
+    if (proc == NULL) {
+        ME("memcheck: Unable to obtain process for pid=%u on free",
+           desc.free_pid);
+        memcheck_fail_free(guest_address);
+        return;
+    }
+
+    // Pull matching entry from the map.
+    pull_res = procdesc_pull_malloc(proc, desc.ptr, &pulled);
+    if (pull_res) {
+        av_invalid_pointer(proc, desc.ptr, 1);
+        memcheck_fail_free(guest_address);
+        return;
+    }
+
+    // Make sure that ptr has expected value
+    if (desc.ptr != mallocdesc_get_user_ptr(&pulled.malloc_desc)) {
+        if (trace_flags & TRACE_CHECK_INVALID_PTR_ENABLED) {
+            printf("memcheck: Access violation is detected in process %s[pid=%u]:\n",
+                   proc->image_path, proc->pid);
+            printf("  INVALID POINTER 0x%08X is used in 'free' operation.\n"
+                   "  This pointer is unexpected for 'free' operation, as allocation\n"
+                   "  descriptor found for this pointer in the process' allocation map\n"
+                   "  suggests that 0x%08X is the pointer to be used to free this block.\n"
+                   "  Allocation descriptor matching the pointer:\n",
+                   desc.ptr,
+                   (uint32_t)mallocdesc_get_user_ptr(&pulled.malloc_desc));
+            memcheck_dump_malloc_desc(&pulled, 1, 0);
+        }
+    }
+    if (pulled.call_stack != NULL) {
+        qemu_free(pulled.call_stack);
+    }
+}
+
+void
+memcheck_guest_query_malloc(target_ulong guest_address)
+{
+    MallocDescQuery qdesc;
+    MallocDescEx* found;
+    ProcDesc* proc;
+
+    // Copy free descriptor from guest to emulator.
+    memcheck_get_query_descriptor(&qdesc, guest_address);
+
+    proc = get_process_from_pid(qdesc.query_pid);
+    if (proc == NULL) {
+        ME("memcheck: Unable to obtain process for pid=%u on query_%s",
+           qdesc.query_pid, qdesc.routine == 1 ? "free" : "realloc");
+        memcheck_fail_query(guest_address);
+        return;
+    }
+
+    // Find allocation entry for the given address.
+    found = procdesc_find_malloc(proc, qdesc.ptr);
+    if (found == NULL) {
+        av_invalid_pointer(proc, qdesc.ptr, qdesc.routine);
+        memcheck_fail_query(guest_address);
+        return;
+    }
+
+    // Copy allocation descriptor back to the guest's space.
+    memcheck_set_malloc_descriptor(qdesc.desc, &found->malloc_desc);
+}
+
+void
+memcheck_guest_print_str(target_ulong str) {
+    char str_copy[4096];
+    memcheck_get_guest_string(str_copy, str, sizeof(str_copy));
+    printf(str_copy);
+}
+
+/* Validates read operations, detected in __ldx_mmu routine.
+ * This routine is called from __ldx_mmu wrapper implemented in
+ * softmmu_template.h on condition that loading is occurring from user memory.
+ * Param:
+ *  addr - Virtual address in the guest space where memory is read.
+ *  data_size - Size of the read.
+ *  retaddr - Code address (in TB) that accesses memory.
+ * Return:
+ *  1 if TLB record for the accessed page should be invalidated in order to
+ *  ensure that subsequent attempts to access data in this page will cause
+ *  __ld/stx_mmu to be used. If memchecker is no longer interested in monitoring
+ * access to this page, this routine returns 0.
+ */
+int
+memcheck_validate_ld(target_ulong addr,
+                     uint32_t data_size,
+                     target_ulong retaddr)
+{
+    ProcDesc* proc;
+    MallocDescEx* desc;
+
+    int res = memcheck_common_access_validation(addr, data_size, &proc, &desc);
+    if (res == -1) {
+        av_access_violation(proc, desc, addr, data_size, 0, retaddr, 1);
+        return 1;
+    }
+
+    /* Even though descriptor for the given address range has not been found,
+     * we need to make sure that pages containing the given address range
+     * don't contain other descriptors. */
+    return res ? procdesc_contains_allocs(proc, addr, data_size) : 0;
+}
+
+/* Validates write operations, detected in __stx_mmu routine.
+ * This routine is called from __stx_mmu wrapper implemented in
+ * softmmu_template.h on condition that storing is occurring from user memory.
+ * Param:
+ *  addr - Virtual address in the guest space where memory is written.
+ *  data_size - Size of the write.
+ *  value - Value to be written. Note that we typecast all values to 64 bits,
+ *      since this will fit all data sizes.
+ *  retaddr - Code address (in TB) that accesses memory.
+ * Return:
+ *  1 if TLB record for the accessed page should be invalidated in order to
+ *  ensure that subsequent attempts to access data in this page will cause
+ *  __ld/stx_mmu to be used. If memchecker is no longer interested in monitoring
+ * access to this page, this routine returns 0.
+ */
+int
+memcheck_validate_st(target_ulong addr,
+                     uint32_t data_size,
+                     uint64_t value,
+                     target_ulong retaddr)
+{
+    MallocDescEx* desc;
+    ProcDesc* proc;
+
+    int res = memcheck_common_access_validation(addr, data_size, &proc, &desc);
+    if (res == -1) {
+        av_access_violation(proc, desc, addr, data_size, value, retaddr, 0);
+        return 1;
+    }
+
+    /* Even though descriptor for the given address range has not been found,
+     * we need to make sure that pages containing the given address range
+     * don't contain other descriptors. */
+    return res ? procdesc_contains_allocs(proc, addr, data_size) : 0;
+}
+
+/* Checks if given address range in the context of the current process is under
+ * surveillance.
+ * Param:
+ *  addr - Starting address of a range.
+ *  size - Range size.
+ * Return:
+ *  boolean: 1 if address range contains memory that require access violation
+ *  detection, or 0 if given address range is in no interest to the memchecker.
+ */
+int
+memcheck_is_checked(target_ulong addr, uint32_t size) {
+    return procdesc_contains_allocs(get_current_process(), addr, size) ? 1 : 0;
+}
diff --git a/memcheck/memcheck.h b/memcheck/memcheck.h
new file mode 100644
index 0000000..a9a6422
--- /dev/null
+++ b/memcheck/memcheck.h
@@ -0,0 +1,194 @@
+/* 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 declarations of types, constants, and routines used by memory
+ * checking framework.
+ */
+
+#ifndef QEMU_MEMCHECK_MEMCHECK_H
+#define QEMU_MEMCHECK_MEMCHECK_H
+
+/* 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 "memcheck_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Initializes memory access checking framework.
+ * This routine is called from emulator's main routine on condition,
+ * that emulator has been started with -memcheck option.
+ * Param:
+ *  tracing_flags - Parameters set for the -memcheck option. These parameters
+ *  contain abbreviation for memchecking tracing messages that should be enabled
+ *  for the emulator and guest systems.
+ */
+void memcheck_init(const char* tracing_flags);
+
+// =============================================================================
+// Handlers for memory allocation events, generated by the guest system.
+// =============================================================================
+
+/* Libc.so has been initialized by a process in guest's system.
+ * This routine is called in response to TRACE_DEV_REG_LIBC_INIT event that is
+ * fired up by the guest system on /dev/qemu_trace mapped page.
+ * Param:
+ *  pid - ID of the process in context of which libc.so has been initialized.
+ */
+void memcheck_guest_libc_initialized(uint32_t pid);
+
+/* Guest system has allocated memory from heap.
+ * This routine is called in response to TRACE_DEV_REG_MALLOC event that is
+ * fired up by the guest system on /dev/qemu_trace mapped page.
+ * Param:
+ *  guest_address - Virtual address of allocation descriptor (MallocDesc) that
+ *      contains information about allocated memory block. Note that this
+ *      descriptor is located in the guests's user memory. Note also that
+ *      emulator reports failure back to the guest by zeroing out libc_pid field
+ *      of the structure, addressed by this parameter.
+ */
+void memcheck_guest_alloc(target_ulong guest_address);
+
+/* Guest system is freeing memory to heap.
+ * This routine is called in response to TRACE_DEV_REG_FREE_PTR event,
+ * fired up by the guest system on /dev/qemu_trace mapped page.
+ * Param:
+ *  guest_address - Virtual address of free descriptor (MallocFree) that
+ *      contains information about memory block that's being freed. Note that
+ *      this descriptor is located in the guests's user memory.  Note also that
+ *      emulator reports failure back to the guest by zeroing out libc_pid field
+ *      of the structure, addressed by this parameter.
+ */
+void memcheck_guest_free(target_ulong guest_address);
+
+/* Guest system has queried information about an address in its virtual memory.
+ * This routine is called in response to TRACE_DEV_REG_QUERY_MALLOC event,
+ * fired up by the guest system on /dev/qemu_trace mapped page.
+ * Param:
+ *  guest_address - Virtual address in the guest's space of the MallocDescQuery
+ *      structure, that describes the query and receives the response. Note
+ *      that emulator reports failure back to the guest by zeroing out libc_pid
+ *      field of the structure, addressed by this parameter.
+ */
+void memcheck_guest_query_malloc(target_ulong guest_address);
+
+/* Prints a string to emulator's stdout.
+ * This routine is called in response to TRACE_DEV_REG_PRINT_USER_STR event,
+ * fired up by the guest system on /dev/qemu_trace mapped page.
+ * Param:
+ *  str - Virtual address in the guest's space of the string to print.
+ */
+void memcheck_guest_print_str(target_ulong str);
+
+// =============================================================================
+// Handlers for events, generated by the kernel.
+// =============================================================================
+
+/* Handles PID initialization event.
+ * This routine is called in response to TRACE_DEV_REG_INIT_PID event, which
+ * indicates that new process has been initialized (but not yet executed).
+ * Param:
+ *  pid - ID of the process that is being initialized. This value will also be
+ *      used as main thread ID for the intializing process.
+ */
+void memcheck_init_pid(uint32_t pid);
+
+/* Handles thread switch event.
+ * This routine is called in response to TRACE_DEV_REG_SWITCH event, which
+ * indicates that thread switch occurred in the guest system.
+ * Param:
+ *  tid - ID of the thread that becomes active.
+ */
+void memcheck_switch(uint32_t tid);
+
+/* Handles process forking / new process creation event.
+ * This routine is called in response to TRACE_DEV_REG_FORK event, which
+ * indicates that new process has been forked / created. It's assumed, that
+ * process that is forking new process is the current process.
+ * Param:
+ *  tgid - TODO: Clarify that!
+ *  new_pid - Process ID that's been assigned to the forked process.
+ */
+void memcheck_fork(uint32_t tgid, uint32_t new_pid);
+
+/* Handles new thread creation event.
+ * This routine is called in response to TRACE_DEV_REG_CLONE event, which
+ * indicates that new thread has been created in context of the current process.
+ * Param:
+ *  tgid - TODO: Clarify that!
+ *  new_tid - Thread ID that's been assigned to the new thread.
+ */
+void memcheck_clone(uint32_t tgid, uint32_t new_tid);
+
+/* Sets process command line.
+ * This routine is called in response to TRACE_DEV_REG_CMDLINE event, which
+ * is used to grab first command line argument, and use it is image path to
+ * the current process.
+ * Param:
+ *  cmg_arg - Command line arguments.
+ *  cmdlen - Length of the command line arguments string.
+ */
+void memcheck_set_cmd_line(const char* cmd_arg, unsigned cmdlen);
+
+/* Handles thread / process exiting event.
+ * This routine is called in response to TRACE_DEV_REG_EXIT event, which
+ * indicates that current thread is exiting. We consider that process is
+ * exiting when last thread for that process is exiting.
+ * Param:
+ *  exit_code - Thread exit code.
+ */
+void memcheck_exit(uint32_t exit_code);
+
+/* Handles memory mapping of a module in guest address space.
+ * This routine is called in response to TRACE_DEV_REG_EXECVE_VMSTART,
+ * TRACE_DEV_REG_EXECVE_VMEND, TRACE_DEV_REG_EXECVE_OFFSET, and
+ * TRACE_DEV_REG_MMAP_EXEPATH events, which indicate that a module has been
+ * loaded and mapped on the guest system.
+ * Param:
+ *  vstart - Guest address where mapping starts.
+ *  vend - Guest address where mapping ends.
+ *  exec_offset - Exec offset inside module mapping.
+ *  path - Path to the module that has been mapped.
+ */
+void memcheck_mmap_exepath(target_ulong vstart,
+                           target_ulong vend,
+                           target_ulong exec_offset,
+                           const char* path);
+
+/* Handles memory unmapping of a module in guest address space.
+ * This routine is called in response to TRACE_DEV_REG_UNMAP_START, and
+ * TRACE_DEV_REG_UNMAP_END events, which indicate that a module has been
+ * unmapped on the guest system.
+ * Param:
+ *  vstart - Guest address where unmapping starts.
+ *  vend - Guest address where unmapping ends.
+ */
+void memcheck_unmap(target_ulong vstart, target_ulong vend);
+
+/* Global flag, indicating whether or not memchecking has been enabled
+ * for the current emulator session. If set to zero, indicates that memchecking
+ * is not enabled. Value other than zero indicates that memchecking is enabled
+ * for the current emulator session.
+ */
+extern int memcheck_enabled;
+
+#ifdef __cplusplus
+};  /* end of extern "C" */
+#endif
+
+#endif  // QEMU_MEMCHECK_MEMCHECK_H
diff --git a/memcheck/memcheck_api.h b/memcheck/memcheck_api.h
new file mode 100644
index 0000000..1961465
--- /dev/null
+++ b/memcheck/memcheck_api.h
@@ -0,0 +1,107 @@
+/* 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 declarations of memchecker external variables and routines, used by
+ * other qemu components.
+ */
+
+#ifndef QEMU_MEMCHECK_MEMCHECK_API_H
+#define QEMU_MEMCHECK_MEMCHECK_API_H
+
+/* 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
+
+/* Global flag, indicating whether or not memchecking has been enabled
+ * for the current emulator session. 1 means that memchecking has been
+ * enabled, 0 means that memchecking has not been enabled. The variable
+ * is declared in memchec/memcheck.c */
+extern int memcheck_enabled;
+
+/* Flags wether or not mmu instrumentation is enabled by memchecker.
+ * 1 - enabled, 0 - is not enabled. */
+extern int memcheck_instrument_mmu;
+
+/* Global flag, indicating whether or not memchecker is collecting call stack.
+ * 1 - call stack is being collected, 0 means that stack is not being
+ * collected. The variable is declared in memchec/memcheck.c */
+extern int memcheck_watch_call_stack;
+
+/* Array of (tb_pc, guest_pc) pairs, big enough for all translations. This
+ * array is used to obtain guest PC address from a translated PC address.
+ * tcg_gen_code_common will fill it up when memchecker is enabled. The array is
+ * declared in ./translate_all.c */
+extern target_ulong* gen_opc_tpc2gpc_ptr;
+
+/* Number of (tb_pc, guest_pc) pairs stored in gen_opc_tpc2gpc array.
+ * The variable is declared in ./translate_all.c */
+extern unsigned int gen_opc_tpc2gpc_pairs;
+
+/* Checks if given address range in the context of the current process is
+ * under surveillance by memchecker.
+ * Param:
+ *  addr - Starting address of a range.
+ *  size - Range size.
+ * Return:
+ *  boolean: 1 if address range contains memory that requires access
+ *  violation detection, or 0 if given address range is in no interest to
+ *  the memchecker. */
+int memcheck_is_checked(target_ulong addr, uint32_t size);
+
+/* Validates __ldx_mmu operations.
+ * Param:
+ *  addr - Virtual address in the guest space where memory is read.
+ *  data_size - Size of the read.
+ *  retaddr - Code address (in TB) that accesses memory.
+ * Return:
+ *  1 Address should be invalidated in TLB cache, in order to ensure that
+ *  subsequent attempts to read from that page will launch __ld/__stx_mmu.
+ *  If this routine returns zero, no page invalidation is requried.
+ */
+int memcheck_validate_ld(target_ulong addr,
+                         uint32_t data_size,
+                         target_ulong retaddr);
+
+/* Validates __stx_mmu operations.
+ * Param:
+ *  addr - Virtual address in the guest space where memory is written.
+ *  data_size - Size of the write.
+ *  value - Value to be written. Note that we typecast all values to 64 bits,
+ *      since this will fit all data sizes.
+ *  retaddr - Code address (in TB) that accesses memory.
+ * Return:
+ *  1 Address should be invalidated in TLB cache, in order to ensure that
+ *  subsequent attempts to read from that page will launch __ld/__stx_mmu.
+ *  If this routine returns zero, no page invalidation is requried.
+ */
+int memcheck_validate_st(target_ulong addr,
+                         uint32_t data_size,
+                         uint64_t value,
+                         target_ulong retaddr);
+
+/* Memchecker's handler for on_call callback.
+ * Param:
+ *  pc - Guest address where call has been made.
+ *  ret - Guest address where called routine will return.
+ */
+void memcheck_on_call(target_ulong pc, target_ulong ret);
+
+/* Memchecker's handler for on_ret callback.
+ * Param:
+ *  pc - Guest address where routine has returned.
+ */
+void memcheck_on_ret(target_ulong pc);
+
+#endif  // QEMU_MEMCHECK_MEMCHECK_API_H
diff --git a/memcheck/memcheck_common.h b/memcheck/memcheck_common.h
new file mode 100644
index 0000000..668b78c
--- /dev/null
+++ b/memcheck/memcheck_common.h
@@ -0,0 +1,484 @@
+/* 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 declarations of structures, routines, etc. that are commonly used
+ * in memechecker framework.
+ */
+
+#ifndef QEMU_MEMCHECK_MEMCHECK_COMMON_H
+#define QEMU_MEMCHECK_MEMCHECK_COMMON_H
+
+/* 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 "qemu-common.h"
+#include "cpu.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// =============================================================================
+// Events generated by the guest system.
+// =============================================================================
+
+/* Notifies the emulator that libc has been initialized for a process.
+ * Event's value parameter is PID for the process in context of which libc has
+ * been initialized.
+ */
+#define TRACE_DEV_REG_LIBC_INIT             1536
+
+/* Notifies the emulator about new memory block being allocated.
+ * Event's value parameter points to MallocDesc instance in the guest's address
+ * space that contains allocated block information. Note that 'libc_pid' field
+ * of the descriptor is used by emulator to report failure in handling this
+ * event. In case of failure emulator will zero that filed before completing
+ * this event.
+ */
+#define TRACE_DEV_REG_MALLOC                1537
+
+/* Notifies the emulator about memory block being freed.
+ * Event's value parameter points to MallocFree descriptor instance in the
+ * guest's address space that contains information about block that's being
+ * freed. Note that 'libc_pid' field of the descriptor is used by emulator to
+ * report failure in handling this event. In case of failure emulator will zero
+ * that filed before completing this event.
+ */
+#define TRACE_DEV_REG_FREE_PTR              1538
+
+/* Queries the emulator about memory block information.
+ * Event's value parameter points to MallocDescQuery descriptor instance in the
+ * guest's address space that contains query parameters. Note that 'libc_pid'
+ * field of the descriptor is used by emulator to report failure in handling
+ * this event. In case of failure emulator will zero that filed before
+ * completing this event.
+ */
+#define TRACE_DEV_REG_QUERY_MALLOC          1539
+
+/* Queries the emulator to print a string to its stdout.
+ * Event's value parameter points to zero-terminated string to be printed. Note
+ * that this string is located in the guest's address space.
+ */
+#define TRACE_DEV_REG_PRINT_USER_STR        1540
+
+// =============================================================================
+// Communication structures
+// =============================================================================
+
+/* Describes memory block allocated from the heap. This structure is passed
+ * along with TRACE_DEV_REG_MALLOC event. This descriptor is used to inform
+ * the emulator about new memory block being allocated from the heap. The entire
+ * structure is initialized by the guest system before event is fired up. It is
+ * important to remember that same structure (an exact copy) is also declared
+ * in the libc's sources. So, every time a change is made to any of these
+ * two declaration, another one must be also updated accordingly. */
+typedef struct MallocDesc {
+    /* Poniter to the memory block actually allocated from the heap. Note that
+     * this is not the pointer that is returned to the malloc's caller. Pointer
+     * returned to the caller is calculated by adding value stored in this field
+     * to the value stored in prefix_size field of this structure.
+     */
+    target_ulong    ptr;
+
+    /* Nuber of bytes requested by the malloc's caller. */
+    uint32_t        requested_bytes;
+
+    /* Byte size of the prefix data. Actual pointer returned to the malloc's
+     * caller is calculated by adding value stored in this field to the value
+     * stored in in the ptr field of this structure.
+     */
+    uint32_t        prefix_size;
+
+    /* Byte size of the suffix data. */
+    uint32_t        suffix_size;
+
+    /* Id of the process that initialized libc instance, in which allocation
+     * has occurred. This field is used by the emulator to report errors in
+     * the course of TRACE_DEV_REG_MALLOC event handling. In case of an error,
+     * emulator sets this field to zero (invalid value for a process ID).
+     */
+    uint32_t        libc_pid;
+
+    /* Id of the process in context of which allocation has occurred.
+     * Value in this field may differ from libc_pid value, if process that
+     * is doing allocation has been forked from the process that initialized
+     * libc instance.
+     */
+    uint32_t        allocator_pid;
+
+    /* Number of access violations detected on this allocation. */
+    uint32_t        av_count;
+} MallocDesc;
+/* Helpers for addressing field in MallocDesc structure, using which emulator
+ * reports an error back to the guest.
+ */
+#define ALLOC_RES_OFFSET        ((uint32_t)&(((MallocDesc*)0)->libc_pid))
+#define ALLOC_RES_ADDRESS(p)    (p + ALLOC_RES_OFFSET)
+
+/* Describes memory block info queried from emulator. This structure is passed
+ * along with TRACE_DEV_REG_QUERY_MALLOC event. When handling free and realloc
+ * calls, it is required that we have information about memory blocks that were
+ * actually allocated in previous calls to malloc, memalign, or realloc. Since
+ * we don't keep this information directlry in the allocated block, but rather
+ * we keep it in the emulator, we need to query emulator for that information
+ * with TRACE_DEV_REG_QUERY_MALLOC query. The entire structure is initialized
+ * by the guest system before event is fired up It is important to remember that
+ * same structure (an exact copy) is also declared in the libc's sources. So,
+ * every time a change is made to any of these two declaration, another one
+ * must be also updated accordingly.
+ */
+typedef struct MallocDescQuery {
+    /* Pointer for which information is queried. Note that this pointer doesn't
+     * have to be exact pointer returned to malloc's caller, but can point
+     * anywhere inside an allocated block, including guarding areas. Emulator
+     * will respond with information about allocated block that contains this
+     * pointer.
+     */
+    target_ulong    ptr;
+
+    /* Id of the process that initialized libc instance, in which this query
+     * is called. This field is used by the emulator to report errors in
+     * the course of TRACE_DEV_REG_QUERY_MALLOC event handling. In case of an
+     * error, emulator sets this field to zero (invalid value for a process ID).
+     */
+    uint32_t        libc_pid;
+
+    /* Process ID in context of which query is made. */
+    uint32_t        query_pid;
+
+    /* Code of the allocation routine, in context of which query has been made:
+     *  1 - free
+     *  2 - realloc
+     */
+    uint32_t        routine;
+
+    /* Address in guest's virtual space of memory allocation descriptor for the
+     * queried pointer. Descriptor, addressed by this field is initialized by
+     * the emulator in response to the query.
+     */
+    target_ulong    desc;
+} MallocDescQuery;
+/* Helpers for addressing field in MallocDescQuery structure using which
+ * emulator reports an error back to the guest.
+ */
+#define QUERY_RES_OFFSET        ((uint32_t)&(((MallocDescQuery*)0)->libc_pid))
+#define QUERY_RES_ADDRESS(p)    (p + QUERY_RES_OFFSET)
+
+/* Describes memory block that is being freed back to the heap. This structure
+ * is passed along with TRACE_DEV_REG_FREE_PTR event. The entire structure is
+ * initialized by the guest system before event is fired up. It is important to
+ * remember that same structure (an exact copy) is also declared in the libc's
+ * sources. So, every time a change is made to any of these two declaration,
+ * another one must be also updated accordingly.
+ */
+typedef struct MallocFree {
+    /* Pointer to be freed. */
+    uint32_t    ptr;
+
+    /* Id of the process that initialized libc instance, in which this free
+     * is called. This field is used by the emulator to report errors in
+     * the course of TRACE_DEV_REG_FREE_PTR event handling. In case of an
+     * error, emulator sets this field to zero (invalid value for a process ID).
+     */
+    uint32_t    libc_pid;
+
+    /* Process ID in context of which memory is being freed. */
+    uint32_t    free_pid;
+} MallocFree;
+/* Helpers for addressing field in MallocFree structure, using which emulator
+ * reports an error back to the guest.
+ */
+#define FREE_RES_OFFSET         ((uint32_t)&(((MallocFree*)0)->libc_pid))
+#define FREE_RES_ADDRESS(p)     (p + FREE_RES_OFFSET)
+
+/* Extends MallocDesc structure with additional information, used by memchecker.
+ */
+typedef struct MallocDescEx {
+    /* Allocation descriptor this structure extends. */
+    MallocDesc      malloc_desc;
+
+    /* Call stack that lead to memory allocation. The array is arranged in
+     * accending order, where entry at index 0 corresponds to the routine
+     * that allocated memory. */
+    target_ulong*   call_stack;
+
+    /* Number of entries in call_stack array. */
+    uint32_t        call_stack_count;
+
+    /* Set of misc. flags. See MDESC_FLAG_XXX bellow. */
+    uint32_t        flags;
+} MallocDescEx;
+
+/* Indicates that memory has been allocated before process started execution.
+ * After a process has been forked, but before it actually starts executing,
+ * allocations can be made in context of that process PID. This flag marks such
+ * allocations in the process' allocation descriptors map.
+ */
+#define MDESC_FLAG_TRANSITION_ENTRY         0x00000001
+
+/* Indicates that memory block has been inherited from the parent process.
+ * When a process is forked from its parent process, the forked process inherits
+ * a copy of the parent process' heap. Thus, all allocations that were recorded
+ * for the parent process must be also recorded for the forked process. This
+ * flag marks entries in the forked process' allocation descriptors map that
+ * were copied over from the parent process' allocation descriptors map.
+ */
+#define MDESC_FLAG_INHERITED_ON_FORK        0x00000002
+
+/* Describes a memory mapping of an execution module in the guest system. */
+typedef struct MMRangeDesc {
+    /* Starting address of mmapping of a module in the guest's address space. */
+    target_ulong            map_start;
+
+    /* Ending address of mmapping of a module in the guest's address space. */
+    target_ulong            map_end;
+
+    /* Mmapping's execution offset. */
+    target_ulong            exec_offset;
+
+    /* Image path of the module that has been mapped with this mmapping. */
+    char*                   path;
+} MMRangeDesc;
+
+/* Enumerates returned values for insert routines implemeted for red-black
+ * tree maps.
+ */
+typedef enum {
+    /* New entry has been inserted into the map. */
+    RBT_MAP_RESULT_ENTRY_INSERTED = 0,
+
+    /* An entry, matching the new one already exists in the map. */
+    RBT_MAP_RESULT_ENTRY_ALREADY_EXISTS,
+
+    /* An existing entry, matching the new one has been replaced
+    * with the new entry.
+    */
+    RBT_MAP_RESULT_ENTRY_REPLACED,
+
+    /* An error has occurred when inserting entry into the map. */
+    RBT_MAP_RESULT_ERROR = -1,
+} RBTMapResult;
+
+/* Encapsulates an array of guest addresses, sorted in accending order. */
+typedef struct AddrArray {
+    /* Array of addresses. */
+    target_ulong*   addr;
+
+    /* Number of elements in the array. */
+    int             num;
+} AddrArray;
+
+// =============================================================================
+// Inlines
+// =============================================================================
+
+/* Gets pointer returned to malloc caller for the given allocation decriptor.
+ * Param:
+ *  desc - Allocation descriptor.
+ * Return:
+ *  Pointer to the allocated memory returned to the malloc caller.
+ */
+static inline target_ulong
+mallocdesc_get_user_ptr(const MallocDesc* desc)
+{
+    return desc->ptr + desc->prefix_size;
+}
+
+/* Gets total size of the allocated block for the given descriptor.
+ * Param:
+ *  desc - Descriptor for the memory block, allocated in malloc handler.
+ * Return:
+ *  Total size of memory block allocated in malloc handler.
+ */
+static inline uint32_t
+mallocdesc_get_alloc_size(const MallocDesc* desc)
+{
+    return desc->prefix_size + desc->requested_bytes + desc->suffix_size;
+}
+
+/* Gets the end of the allocated block for the given descriptor.
+ * Param:
+ *  desc - Descriptor for the memory block, allocated in malloc handler.
+ * Return:
+ *  Pointer to the end of the allocated block (next byte past the block).
+ */
+static inline target_ulong
+mallocdesc_get_alloc_end(const MallocDesc* desc)
+{
+    return desc->ptr + mallocdesc_get_alloc_size(desc);
+}
+
+/* Gets the end of the allocated block available to the user for the given
+ * descriptor.
+ * Param:
+ *  desc - Descriptor for the memory block, allocated in malloc handler.
+ * Return:
+ *  Pointer to the end of the allocated block available to the user (next byte
+ *  past the block - suffix guarding area).
+ */
+static inline target_ulong
+mallocdesc_get_user_alloc_end(const MallocDesc* desc)
+{
+    return mallocdesc_get_user_ptr(desc) + desc->requested_bytes;
+}
+
+/* Checks if allocation has been made before process started execution.
+ * Param:
+ *  desc - Allocation descriptor to check.
+ * Return:
+ *  boolean: 1 if allocation has been made before process started execution,
+ *  or 0 if allocation has been made after process started execution.
+ */
+static inline int
+mallocdescex_is_transition_entry(const MallocDescEx* desc)
+{
+    return (desc->flags & MDESC_FLAG_TRANSITION_ENTRY) != 0;
+}
+
+/* Checks if allocation block has been inherited on fork.
+ * Param:
+ *  desc - Allocation descriptor to check.
+ * Return:
+ *  boolean: 1 if allocation has been inherited on fork, or 0 if allocation
+ *  has been made by this process..
+ */
+static inline int
+mallocdescex_is_inherited_on_fork(const MallocDescEx* desc)
+{
+    return (desc->flags & MDESC_FLAG_INHERITED_ON_FORK) != 0;
+}
+
+/* Gets offset for the given address inside a mapped module.
+ * Param:
+ *  address - Address to get offset for.
+ * Return:
+ *  Offset of the given address inside a mapped module, represented with the
+ *  given mmaping range descriptor.
+ */
+static inline target_ulong
+mmrangedesc_get_module_offset(const MMRangeDesc* rdesc, target_ulong address)
+{
+    return address - rdesc->map_start + rdesc->exec_offset;
+}
+
+/* Checks if given address is contained in the given address array.
+ * Return:
+ *  boolean: 1 if address is contained in the array, or zero if it's not.
+ */
+static inline int
+addrarray_check(const AddrArray* addr_array, target_ulong addr)
+{
+    if (addr_array->num != 0) {
+        int m_min = 0;
+        int m_max = addr_array->num - 1;
+
+        /* May be odd for THUMB mode. */
+        addr &= ~1;
+        /* Since array is sorted we can do binary search here. */
+        while (m_min <= m_max) {
+            const int m = (m_min + m_max) >> 1;
+            const target_ulong saved = addr_array->addr[m];
+            if (addr == saved) {
+                return 1;
+            }
+            if (addr < saved) {
+                m_max = m - 1;
+            } else {
+                m_min = m + 1;
+            }
+        }
+    }
+    return 0;
+}
+
+/* Adds an address to the address array.
+ * Return:
+ *  1  - Address has been added to the array.
+ *  -1 - Address already exists in the array.
+ *  0  - Unable to expand the array.
+ */
+static inline int
+addrarray_add(AddrArray* addr_array, target_ulong addr)
+{
+    target_ulong* new_arr;
+    int m_min;
+    int m_max;
+
+    /* May be odd for THUMB mode. */
+    addr &= ~1;
+    if (addr_array->num == 0) {
+        /* First element. */
+        addr_array->addr = qemu_malloc(sizeof(target_ulong));
+        assert(addr_array->addr != NULL);
+        if (addr_array->addr == NULL) {
+            return 0;
+        }
+        *addr_array->addr = addr;
+        addr_array->num++;
+        return 1;
+    }
+
+    /* Using binary search find the place where to insert new address. */
+    m_min = 0;
+    m_max = addr_array->num - 1;
+    while (m_min <= m_max) {
+        const int m = (m_min + m_max) >> 1;
+        const target_ulong saved = addr_array->addr[m];
+        if (addr == saved) {
+            return -1;
+        }
+        if (addr < saved) {
+            m_max = m - 1;
+        } else {
+            m_min = m + 1;
+        }
+    }
+    if (m_max < 0) {
+        m_max = 0;
+    }
+    /* Expand the array. */
+    new_arr = qemu_malloc(sizeof(target_ulong) * (addr_array->num + 1));
+    assert(new_arr != NULL);
+    if (new_arr == NULL) {
+        return 0;
+    }
+    /* Copy preceding elements to the new array. */
+    if (m_max != 0) {
+        memcpy(new_arr, addr_array->addr, m_max * sizeof(target_ulong));
+    }
+    if (addr > addr_array->addr[m_max]) {
+        new_arr[m_max] = addr_array->addr[m_max];
+        m_max++;
+    }
+    /* Insert new address. */
+    new_arr[m_max] = addr;
+    /* Copy remaining elements to the new array. */
+    if (m_max < addr_array->num) {
+        memcpy(new_arr + m_max + 1, addr_array->addr + m_max,
+               (addr_array->num - m_max) * sizeof(target_ulong));
+    }
+    /* Swap arrays. */
+    qemu_free(addr_array->addr);
+    addr_array->addr = new_arr;
+    addr_array->num++;
+    return 1;
+}
+
+#ifdef __cplusplus
+};  /* end of extern "C" */
+#endif
+
+#endif  // QEMU_MEMCHECK_MEMCHECK_COMMON_H
diff --git a/memcheck/memcheck_logging.h b/memcheck/memcheck_logging.h
new file mode 100644
index 0000000..c2ae6e9
--- /dev/null
+++ b/memcheck/memcheck_logging.h
@@ -0,0 +1,94 @@
+/* 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 declarations of logging macros used in memchecker framework.
+ */
+
+#ifndef QEMU_MEMCHECK_MEMCHECK_LOGGING_H
+#define QEMU_MEMCHECK_MEMCHECK_LOGGING_H
+
+/* 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 "qemu-common.h"
+#include "android/utils/debug.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Prints debug message under the 'memcheck' tag. */
+#define MD(...)  VERBOSE_PRINT(memcheck, __VA_ARGS__)
+
+/* Prints an error message under the 'memcheck' tag. */
+#define ME(...)  \
+    do { if (VERBOSE_CHECK(memcheck)) derror(__VA_ARGS__); } while (0)
+
+// =============================================================================
+// Tracing flags (see trace_flags declared in memcheck.c), and macros
+// =============================================================================
+
+/* Enables fork() tracing. */
+#define TRACE_PROC_FORK_ENABLED                 0x00000001
+/* Enables clone() tracing. */
+#define TRACE_PROC_CLONE_ENABLED                0x00000002
+/* Enables new PID allocation tracing. */
+#define TRACE_PROC_NEW_PID_ENABLED              0x00000004
+/* Enables guest process starting tracing. */
+#define TRACE_PROC_START_ENABLED                0x00000008
+/* Enables guest process exiting tracing. */
+#define TRACE_PROC_EXIT_ENABLED                 0x00000010
+/* Enables libc.so initialization tracing. */
+#define TRACE_PROC_LIBC_INIT_ENABLED            0x00000020
+/* Enables leaking tracing. */
+#define TRACE_CHECK_LEAK_ENABLED                0x00000040
+/* Enables invalid pointer access tracing. */
+#define TRACE_CHECK_INVALID_PTR_ENABLED         0x00000080
+/* Enables reading violations tracing. */
+#define TRACE_CHECK_READ_VIOLATION_ENABLED      0x00000100
+/* Enables writing violations tracing. */
+#define TRACE_CHECK_WRITE_VIOLATION_ENABLED     0x00000200
+/* Enables module mapping tracing. */
+#define TRACE_PROC_MMAP_ENABLED                 0x00000400
+/* All tracing flags combined. */
+#define TRACE_ALL_ENABLED   (TRACE_PROC_FORK_ENABLED                | \
+                             TRACE_PROC_CLONE_ENABLED               | \
+                             TRACE_PROC_NEW_PID_ENABLED             | \
+                             TRACE_PROC_START_ENABLED               | \
+                             TRACE_PROC_LIBC_INIT_ENABLED           | \
+                             TRACE_PROC_EXIT_ENABLED                | \
+                             TRACE_CHECK_INVALID_PTR_ENABLED        | \
+                             TRACE_CHECK_READ_VIOLATION_ENABLED     | \
+                             TRACE_CHECK_WRITE_VIOLATION_ENABLED    | \
+                             TRACE_PROC_MMAP_ENABLED                | \
+                             TRACE_CHECK_LEAK_ENABLED)
+
+/* Prints a trace to the stdout. */
+#define T(level, ...)                                   \
+    do {                                                \
+        if (trace_flags & TRACE_##level##_ENABLED) {    \
+            printf(__VA_ARGS__);                        \
+        }                                               \
+    } while (0)
+
+/* Set of tracing flags (declared in memchek.c). */
+extern uint32_t trace_flags;
+
+#ifdef __cplusplus
+};  /* end of extern "C" */
+#endif
+
+#endif  // QEMU_MEMCHECK_MEMCHECK_LOGGING_H
diff --git a/memcheck/memcheck_malloc_map.c b/memcheck/memcheck_malloc_map.c
new file mode 100644
index 0000000..07ae889
--- /dev/null
+++ b/memcheck/memcheck_malloc_map.c
@@ -0,0 +1,289 @@
+/* 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 routines that implement a red-black tree of
+ * memory blocks allocated by the guest system.
+ */
+
+/* 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 "memcheck_malloc_map.h"
+#include "memcheck_util.h"
+#include "memcheck_logging.h"
+
+/* Global flag, indicating whether or not __ld/__stx_mmu should be instrumented
+ * for checking for access violations. If read / write access violation check.
+ * Defined in memcheck.c
+ */
+extern int memcheck_instrument_mmu;
+
+/* Allocation descriptor stored in the map. */
+typedef struct AllocMapEntry {
+    /* R-B tree entry. */
+    RB_ENTRY(AllocMapEntry) rb_entry;
+
+    /* Allocation descriptor for this entry. */
+    MallocDescEx            desc;
+} AllocMapEntry;
+
+// =============================================================================
+// Inlines
+// =============================================================================
+
+/* Gets address of the beginning of an allocation block for the given entry in
+ * the map.
+ * Param:
+ *  adesc - Entry in the allocation descriptors map.
+ * Return:
+ *  Address of the beginning of an allocation block for the given entry in the
+ *  map.
+ */
+static inline target_ulong
+allocmapentry_alloc_begins(const AllocMapEntry* adesc)
+{
+    return adesc->desc.malloc_desc.ptr;
+}
+
+/* Gets address of the end of an allocation block for the given entry in
+ * the map.
+ * Param:
+ *  adesc - Entry in the allocation descriptors map.
+ * Return:
+ *  Address of the end of an allocation block for the given entry in the map.
+ */
+static inline target_ulong
+allocmapentry_alloc_ends(const AllocMapEntry* adesc)
+{
+    return mallocdesc_get_alloc_end(&adesc->desc.malloc_desc);
+}
+
+// =============================================================================
+// R-B Tree implementation
+// =============================================================================
+
+/* Compare routine for the allocation descriptors map.
+ * Param:
+ *  d1 - First map entry to compare.
+ *  d2 - Second map entry to compare.
+ * Return:
+ *  0 - Descriptors are equal. Note that descriptors are considered to be
+ *      equal iff memory blocks they describe intersect in any part.
+ *  1 - d1 is greater than d2
+ *  -1 - d1 is less than d2.
+ */
+static inline int
+cmp_rb(AllocMapEntry* d1, AllocMapEntry* d2)
+{
+    const target_ulong start1 = allocmapentry_alloc_begins(d1);
+    const target_ulong start2 = allocmapentry_alloc_begins(d2);
+
+    if (start1 < start2) {
+        return (allocmapentry_alloc_ends(d1) - 1) < start2 ? -1 : 0;
+    }
+    return (allocmapentry_alloc_ends(d2) - 1) < start1 ? 1 : 0;
+}
+
+/* Expands RB macros here. */
+RB_GENERATE(AllocMap, AllocMapEntry, rb_entry, cmp_rb);
+
+// =============================================================================
+// Static routines
+// =============================================================================
+
+/* Inserts new (or replaces existing) entry into allocation descriptors map.
+ * See comments on allocmap_insert routine in the header file for details
+ * about this routine.
+ */
+static RBTMapResult
+allocmap_insert_desc(AllocMap* map,
+                     AllocMapEntry* adesc,
+                     MallocDescEx* replaced)
+{
+    AllocMapEntry* existing = AllocMap_RB_INSERT(map, adesc);
+    if (existing == NULL) {
+        return RBT_MAP_RESULT_ENTRY_INSERTED;
+    }
+
+    // Matching entry exists. Lets see if we need to replace it.
+    if (replaced == NULL) {
+        return RBT_MAP_RESULT_ENTRY_ALREADY_EXISTS;
+    }
+
+    /* Copy existing entry to the provided buffer and replace it
+     * with the new one. */
+    memcpy(replaced, &existing->desc, sizeof(MallocDescEx));
+    AllocMap_RB_REMOVE(map, existing);
+    qemu_free(existing);
+    AllocMap_RB_INSERT(map, adesc);
+    return RBT_MAP_RESULT_ENTRY_REPLACED;
+}
+
+/* Finds an entry in the allocation descriptors map that matches the given
+ * address range.
+ * Param:
+ *  map - Allocation descriptors map where to search for an entry.
+ *  address - Virtual address in the guest's user space to find matching
+ *      entry for.
+ * Return:
+ *  Address of an allocation descriptors map entry that matches the given
+ *  address, or NULL if no such entry has been found.
+ */
+static inline AllocMapEntry*
+allocmap_find_entry(const AllocMap* map,
+                    target_ulong address,
+                    uint32_t block_size)
+{
+    AllocMapEntry adesc;
+    adesc.desc.malloc_desc.ptr = address;
+    adesc.desc.malloc_desc.requested_bytes = block_size;
+    adesc.desc.malloc_desc.prefix_size = 0;
+    adesc.desc.malloc_desc.suffix_size = 0;
+    return AllocMap_RB_FIND((AllocMap*)map, &adesc);
+}
+
+// =============================================================================
+// Map API
+// =============================================================================
+
+void
+allocmap_init(AllocMap* map)
+{
+    RB_INIT(map);
+}
+
+RBTMapResult
+allocmap_insert(AllocMap* map, const MallocDescEx* desc, MallocDescEx* replaced)
+{
+    RBTMapResult ret;
+
+    // Allocate and initialize new map entry.
+    AllocMapEntry* adesc = qemu_malloc(sizeof(AllocMapEntry));
+    if (adesc == NULL) {
+        ME("memcheck: Unable to allocate new AllocMapEntry on insert.");
+        return RBT_MAP_RESULT_ERROR;
+    }
+    memcpy(&adesc->desc, desc, sizeof(MallocDescEx));
+
+    // Insert new entry into the map.
+    ret = allocmap_insert_desc(map, adesc, replaced);
+    if (ret == RBT_MAP_RESULT_ENTRY_ALREADY_EXISTS ||
+        ret == RBT_MAP_RESULT_ERROR) {
+        /* Another descriptor already exists for this block, or an error
+         * occurred. We have to tree new descriptor, as it wasn't inserted. */
+        qemu_free(adesc);
+    }
+    return ret;
+}
+
+MallocDescEx*
+allocmap_find(const AllocMap* map, target_ulong address, uint32_t block_size)
+{
+    AllocMapEntry* adesc = allocmap_find_entry(map, address, block_size);
+    return adesc != NULL ? &adesc->desc : NULL;
+}
+
+int
+allocmap_pull(AllocMap* map, target_ulong address, MallocDescEx* pulled)
+{
+    AllocMapEntry* adesc = allocmap_find_entry(map, address, 1);
+    if (adesc != NULL) {
+        memcpy(pulled, &adesc->desc, sizeof(MallocDescEx));
+        AllocMap_RB_REMOVE(map, adesc);
+        qemu_free(adesc);
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+int
+allocmap_pull_first(AllocMap* map, MallocDescEx* pulled)
+{
+    AllocMapEntry* first = RB_MIN(AllocMap, map);
+    if (first != NULL) {
+        memcpy(pulled, &first->desc, sizeof(MallocDescEx));
+        AllocMap_RB_REMOVE(map, first);
+        qemu_free(first);
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+int
+allocmap_copy(AllocMap* to,
+              const AllocMap* from,
+              uint32_t set_flags,
+              uint32_t clear_flags)
+{
+    AllocMapEntry* entry;
+    RB_FOREACH(entry, AllocMap, (AllocMap*)from) {
+        RBTMapResult ins_res;
+        AllocMapEntry* new_entry =
+                (AllocMapEntry*)qemu_malloc(sizeof(AllocMapEntry));
+        if (new_entry == NULL) {
+            ME("memcheck: Unable to allocate new AllocMapEntry on copy.");
+            return -1;
+        }
+        memcpy(new_entry, entry, sizeof(AllocMapEntry));
+        new_entry->desc.flags &= ~clear_flags;
+        new_entry->desc.flags |= set_flags;
+        if (entry->desc.call_stack_count) {
+            new_entry->desc.call_stack =
+               qemu_malloc(entry->desc.call_stack_count * sizeof(target_ulong));
+            memcpy(new_entry->desc.call_stack, entry->desc.call_stack,
+                   entry->desc.call_stack_count * sizeof(target_ulong));
+        } else {
+            new_entry->desc.call_stack = NULL;
+        }
+        new_entry->desc.call_stack_count = entry->desc.call_stack_count;
+        ins_res = allocmap_insert_desc(to, new_entry, NULL);
+        if (ins_res == RBT_MAP_RESULT_ENTRY_INSERTED) {
+            if (memcheck_instrument_mmu) {
+                // Invalidate TLB cache for inserted entry.
+                invalidate_tlb_cache(new_entry->desc.malloc_desc.ptr,
+                        mallocdesc_get_alloc_end(&new_entry->desc.malloc_desc));
+            }
+        } else {
+            ME("memcheck: Unable to insert new map entry on copy. Insert returned %u",
+               ins_res);
+            if (new_entry->desc.call_stack != NULL) {
+                qemu_free(new_entry->desc.call_stack);
+            }
+            qemu_free(new_entry);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+int
+allocmap_empty(AllocMap* map)
+{
+    MallocDescEx pulled;
+    int removed = 0;
+
+    while (!allocmap_pull_first(map, &pulled)) {
+        removed++;
+        if (pulled.call_stack != NULL) {
+            qemu_free(pulled.call_stack);
+        }
+    }
+
+    return removed;
+}
diff --git a/memcheck/memcheck_malloc_map.h b/memcheck/memcheck_malloc_map.h
new file mode 100644
index 0000000..b356180
--- /dev/null
+++ b/memcheck/memcheck_malloc_map.h
@@ -0,0 +1,150 @@
+/* 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 declarations of structures and routines that implement a red-black
+ * tree (a map) of memory blocks allocated by the guest system. The map is
+ * organized in such a way, that each entry in the map describes a virtual
+ * address range that belongs to a memory block allocated in the guest's space.
+ * The range includes block's suffix and prefix, as well as block returned to
+ * malloc's caller. Map considers two blocks to be equal if their address ranges
+ * intersect in any part. Allocation descriptor maps are instantiated one per
+ * each process running on the guest system.
+ */
+
+#ifndef QEMU_MEMCHECK_MEMCHECK_MALLOC_MAP_H
+#define QEMU_MEMCHECK_MEMCHECK_MALLOC_MAP_H
+
+/* 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 "sys-tree.h"
+#include "memcheck_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Allocation descriptors map. */
+typedef struct AllocMap {
+    /* Head of the map. */
+    struct AllocMapEntry*   rbh_root;
+} AllocMap;
+
+// =============================================================================
+// Map API
+// =============================================================================
+
+/* Initializes allocation descriptors map.
+ * Param:
+ *  map - Allocation descriptors map to initialize.
+ */
+void allocmap_init(AllocMap* map);
+
+/* Inserts new (or replaces existing) entry in the allocation descriptors map.
+ * Insertion, or replacement is controlled by the value, passed to this routine
+ * with 'replaced' parameter. If this parameter is NULL, insertion will fail if
+ * a matching entry already exists in the map. If 'replaced' parameter is not
+ * NULL, and a matching entry exists in the map, content of the existing entry
+ * will be copied to the descriptor, addressed by 'replace' parameter, existing
+ * entry will be removed from the map, and new entry will be inserted.
+ * Param:
+ *  map - Allocation descriptors map where to insert new, or replace existing
+ *      entry.
+ *  desc - Allocation descriptor to insert to the map.
+ *  replaced - If not NULL, upon return from this routine contains descriptor
+ *      that has been replaced in the map with the new entry. Note that if this
+ *      routine returns with value other than RBT_MAP_RESULT_ENTRY_REPLACED,
+ *      content of the 'replaced' buffer is not defined, as no replacement has
+ *      actually occurred.
+ * Return
+ *  See RBTMapResult for the return codes.
+ */
+RBTMapResult allocmap_insert(AllocMap* map,
+                             const MallocDescEx* desc,
+                             MallocDescEx* replaced);
+
+/* Finds an entry in the allocation descriptors map that matches the given
+ * address.
+ * Param:
+ *  map - Allocation descriptors map where to search for an entry.
+ *  address - Virtual address in the guest's user space to find matching
+ *      entry for. Entry matches the address, if address is contained within
+ *      allocated memory range (including guarding areas), as defined by the
+ *      memory allocation descriptor for that entry.
+ *  block_size - Size of the block, beginning with 'address'.
+ * Return:
+ *  Pointer to the allocation descriptor found in a map entry, or NULL if no
+ *  matching entry has been found in the map.
+ */
+MallocDescEx* allocmap_find(const AllocMap* map,
+                            target_ulong address,
+                            uint32_t block_size);
+
+/* Pulls (finds and removes) an entry from the allocation descriptors map that
+ * matches the given address.
+ * Param:
+ *  map - Allocation descriptors map where to search for an entry.
+ *  address - Virtual address in the guest's user space to find matching
+ *      entry for. Entry matches the address, if address is contained within
+ *      allocated memory range (including guarding areas), as defined by the
+ *      memory allocation descriptor for that entry.
+ *  pulled - Upon successful return contains allocation descriptor data pulled
+ *      from the map.
+ * Return:
+ *  Zero if an allocation descriptor that matches the given address has
+ *  been pulled, or 1 if no matching entry has been found in the map.
+ */
+int allocmap_pull(AllocMap* map, target_ulong address, MallocDescEx* pulled);
+
+/* Pulls (removes) an entry from the head of the allocation descriptors map.
+ * Param:
+ *  map - Allocation descriptors map where to pull an entry from.
+ *  pulled - Upon successful return contains allocation descriptor data pulled
+ *      from the head of the map.
+ * Return:
+ *  Zero if an allocation descriptor has been pulled from the head of the map,
+ *  or 1 if map is empty.
+ */
+int allocmap_pull_first(AllocMap* map, MallocDescEx* pulled);
+
+/* Copies content of one memory allocation descriptors map to another.
+ * Param:
+ *  to - Map where to copy entries to.
+ *  from - Map where to copy entries from.
+ *  set_flags - Flags that should be set in the copied entry's 'flags' field.
+ *  celar_flags - Flags that should be cleared in the copied entry's 'flags'
+ *  field.
+ * Return:
+ *  Zero on success, or -1 on error.
+ */
+int allocmap_copy(AllocMap* to,
+                  const AllocMap* from,
+                  uint32_t set_flags,
+                  uint32_t clear_flags);
+
+/* Empties the map.
+ * Param:
+ *  map - Map to empty.
+ * Return:
+ *  Number of entries removed from the map.
+ */
+int allocmap_empty(AllocMap* map);
+
+#ifdef __cplusplus
+};  /* end of extern "C" */
+#endif
+
+#endif  // QEMU_MEMCHECK_MEMCHECK_MALLOC_MAP_H
diff --git a/memcheck/memcheck_mmrange_map.c b/memcheck/memcheck_mmrange_map.c
new file mode 100644
index 0000000..f2609df
--- /dev/null
+++ b/memcheck/memcheck_mmrange_map.c
@@ -0,0 +1,236 @@
+/* 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 routines that implement a red-black tree of
+ * memory mappings in the guest system.
+ */
+
+/* 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 "memcheck_mmrange_map.h"
+#include "memcheck_logging.h"
+
+/* Memory range descriptor stored in the map. */
+typedef struct MMRangeMapEntry {
+    /* R-B tree entry. */
+    RB_ENTRY(MMRangeMapEntry)   rb_entry;
+
+    /* Memory range descriptor for this entry. */
+    MMRangeDesc                 desc;
+} MMRangeMapEntry;
+
+// =============================================================================
+// R-B Tree implementation
+// =============================================================================
+
+/* Compare routine for the map.
+ * Param:
+ *  d1 - First map entry to compare.
+ *  d2 - Second map entry to compare.
+ * Return:
+ *  0 - Descriptors are equal. Note that descriptors are considered to be
+ *      equal iff memory blocks they describe intersect in any part.
+ *  1 - d1 is greater than d2
+ *  -1 - d1 is less than d2.
+ */
+static inline int
+cmp_rb(MMRangeMapEntry* d1, MMRangeMapEntry* d2)
+{
+    const target_ulong start1 = d1->desc.map_start;
+    const target_ulong start2 = d2->desc.map_start;
+
+    if (start1 < start2) {
+        return (d1->desc.map_end - 1) < start2 ? -1 : 0;
+    }
+    return (d2->desc.map_end - 1) < start1 ? 1 : 0;
+}
+
+/* Expands RB macros here. */
+RB_GENERATE(MMRangeMap, MMRangeMapEntry, rb_entry, cmp_rb);
+
+// =============================================================================
+// Static routines
+// =============================================================================
+
+/* Inserts new (or replaces existing) entry into the map.
+ * See comments on mmrangemap_insert routine in the header file for details
+ * about this routine.
+ */
+static RBTMapResult
+mmrangemap_insert_desc(MMRangeMap* map,
+                       MMRangeMapEntry* rdesc,
+                       MMRangeDesc* replaced)
+{
+    MMRangeMapEntry* existing = MMRangeMap_RB_INSERT(map, rdesc);
+    if (existing == NULL) {
+        return RBT_MAP_RESULT_ENTRY_INSERTED;
+    }
+
+    // Matching entry exists. Lets see if we need to replace it.
+    if (replaced == NULL) {
+        return RBT_MAP_RESULT_ENTRY_ALREADY_EXISTS;
+    }
+
+    /* Copy existing entry to the provided buffer and replace it
+     * with the new one. */
+    memcpy(replaced, &existing->desc, sizeof(MMRangeDesc));
+    MMRangeMap_RB_REMOVE(map, existing);
+    qemu_free(existing);
+    MMRangeMap_RB_INSERT(map, rdesc);
+    return RBT_MAP_RESULT_ENTRY_REPLACED;
+}
+
+/* Finds an entry in the map that matches the given address range.
+ * Param:
+ *  map - Map where to search for an entry.
+ *  start - Starting address of a mapping range.
+ *  end - Ending address of a mapping range.
+ * Return:
+ *  Address of a map entry that matches the given range, or NULL if no
+ *  such entry has been found.
+ */
+static inline MMRangeMapEntry*
+mmrangemap_find_entry(const MMRangeMap* map,
+                      target_ulong start,
+                      target_ulong end)
+{
+    MMRangeMapEntry rdesc;
+    rdesc.desc.map_start = start;
+    rdesc.desc.map_end = end;
+    return MMRangeMap_RB_FIND((MMRangeMap*)map, &rdesc);
+}
+
+// =============================================================================
+// Map API
+// =============================================================================
+
+void
+mmrangemap_init(MMRangeMap* map)
+{
+    RB_INIT(map);
+}
+
+RBTMapResult
+mmrangemap_insert(MMRangeMap* map,
+                  const MMRangeDesc* desc,
+                  MMRangeDesc* replaced)
+{
+    RBTMapResult ret;
+
+    // Allocate and initialize new map entry.
+    MMRangeMapEntry* rdesc = qemu_malloc(sizeof(MMRangeMapEntry));
+    if (rdesc == NULL) {
+        ME("memcheck: Unable to allocate new MMRangeMapEntry on insert.");
+        return RBT_MAP_RESULT_ERROR;
+    }
+    memcpy(&rdesc->desc, desc, sizeof(MMRangeDesc));
+
+    // Insert new entry into the map.
+    ret = mmrangemap_insert_desc(map, rdesc, replaced);
+    if (ret == RBT_MAP_RESULT_ENTRY_ALREADY_EXISTS ||
+        ret == RBT_MAP_RESULT_ERROR) {
+        /* Another descriptor already exists for this block, or an error
+         * occurred. We have to free new descriptor, as it wasn't inserted. */
+        qemu_free(rdesc);
+    }
+    return ret;
+}
+
+MMRangeDesc*
+mmrangemap_find(const MMRangeMap* map, target_ulong start, target_ulong end)
+{
+    MMRangeMapEntry* rdesc = mmrangemap_find_entry(map, start, end);
+    return rdesc != NULL ? &rdesc->desc : NULL;
+}
+
+int
+mmrangemap_pull(MMRangeMap* map,
+                target_ulong start,
+                target_ulong end,
+                MMRangeDesc* pulled)
+{
+    MMRangeMapEntry* rdesc = mmrangemap_find_entry(map, start, end);
+    if (rdesc != NULL) {
+        memcpy(pulled, &rdesc->desc, sizeof(MMRangeDesc));
+        MMRangeMap_RB_REMOVE(map, rdesc);
+        qemu_free(rdesc);
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+int
+mmrangemap_pull_first(MMRangeMap* map, MMRangeDesc* pulled)
+{
+    MMRangeMapEntry* first = RB_MIN(MMRangeMap, map);
+    if (first != NULL) {
+        memcpy(pulled, &first->desc, sizeof(MMRangeDesc));
+        MMRangeMap_RB_REMOVE(map, first);
+        qemu_free(first);
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+int
+mmrangemap_copy(MMRangeMap* to, const MMRangeMap* from)
+{
+    MMRangeMapEntry* entry;
+    RB_FOREACH(entry, MMRangeMap, (MMRangeMap*)from) {
+        RBTMapResult ins_res;
+        MMRangeMapEntry* new_entry =
+                (MMRangeMapEntry*)qemu_malloc(sizeof(MMRangeMapEntry));
+        if (new_entry == NULL) {
+            ME("memcheck: Unable to allocate new MMRangeMapEntry on copy.");
+            return -1;
+        }
+        memcpy(new_entry, entry, sizeof(MMRangeMapEntry));
+        new_entry->desc.path = qemu_malloc(strlen(entry->desc.path) + 1);
+        if (new_entry->desc.path == NULL) {
+            ME("memcheck: Unable to allocate new path for MMRangeMapEntry on copy.");
+            qemu_free(new_entry);
+            return -1;
+        }
+        strcpy(new_entry->desc.path, entry->desc.path);
+        ins_res = mmrangemap_insert_desc(to, new_entry, NULL);
+        if (ins_res != RBT_MAP_RESULT_ENTRY_INSERTED) {
+            ME("memcheck: Unable to insert new range map entry on copy. Insert returned %u",
+               ins_res);
+            qemu_free(new_entry->desc.path);
+            qemu_free(new_entry);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+int
+mmrangemap_empty(MMRangeMap* map)
+{
+    MMRangeDesc pulled;
+    int removed = 0;
+
+    while (!mmrangemap_pull_first(map, &pulled)) {
+        qemu_free(pulled.path);
+        removed++;
+    }
+
+    return removed;
+}
diff --git a/memcheck/memcheck_mmrange_map.h b/memcheck/memcheck_mmrange_map.h
new file mode 100644
index 0000000..f2c9701
--- /dev/null
+++ b/memcheck/memcheck_mmrange_map.h
@@ -0,0 +1,127 @@
+/* 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 declarations of structures and routines that implement a red-black
+ * tree (a map) of module memory mapping ranges in the guest system. The map is
+ * organized in such a way, that each entry in the map describes a virtual
+ * address range that belongs to a mapped execution module in the guest system.
+ * Map considers two ranges to be equal if their address ranges intersect in
+ * any part.
+ */
+
+#ifndef QEMU_MEMCHECK_MEMCHECK_MMRANGE_MAP_H
+#define QEMU_MEMCHECK_MEMCHECK_MMRANGE_MAP_H
+
+/* 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 "sys-tree.h"
+#include "memcheck_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Memory mapping range map. */
+typedef struct MMRangeMap {
+    /* Head of the map. */
+    struct MMRangeMapEntry* rbh_root;
+} MMRangeMap;
+
+// =============================================================================
+// Map API
+// =============================================================================
+
+/* Initializes the map.
+ * Param:
+ *  map - Map to initialize.
+ */
+void mmrangemap_init(MMRangeMap* map);
+
+/* Inserts new (or replaces existing) entry in the map.
+ * Insertion, or replacement is controlled by the value, passed to this routine
+ * with 'replaced' parameter. If this parameter is NULL, insertion will fail if
+ * a matching entry already exists in the map. If 'replaced' parameter is not
+ * NULL, and a matching entry exists in the map, content of the existing entry
+ * will be copied to the descriptor, addressed by 'replace' parameter, existing
+ * entry will be removed from the map, and new entry will be inserted.
+ * Param:
+ *  map - Map where to insert new, or replace existing entry.
+ *  desc - Descriptor to insert to the map.
+ *  replaced - If not NULL, upon return from this routine contains descriptor
+ *      that has been replaced in the map with the new entry. Note that if this
+ *      routine returns with value other than RBT_MAP_RESULT_ENTRY_REPLACED,
+ *      content of the 'replaced' buffer is not defined, as no replacement has
+ *      actually occurred.
+ * Return
+ *  See RBTMapResult for the return codes.
+ */
+RBTMapResult mmrangemap_insert(MMRangeMap* map,
+                               const MMRangeDesc* desc,
+                               MMRangeDesc* replaced);
+
+/* Finds an entry in the map that matches the given address.
+ * Param:
+ *  map - Map where to search for an entry.
+ *  start - Starting address of a mapping range.
+ *  end - Ending address of a mapping range.
+ * Return:
+ *  Pointer to the descriptor found in a map entry, or NULL if no matching
+ * entry has been found in the map.
+ */
+MMRangeDesc* mmrangemap_find(const MMRangeMap* map,
+                             target_ulong start,
+                             target_ulong end);
+
+/* Pulls (finds and removes) an entry from the map that matches the given
+ * address.
+ * Param:
+ *  map - Map where to search for an entry.
+ *  start - Starting address of a mapping range.
+ *  end - Ending address of a mapping range.
+ *  pulled - Upon successful return contains descriptor data pulled from the
+ *      map.
+ * Return:
+ *  Zero if a descriptor that matches the given address has been pulled, or 1
+ *  if no matching entry has been found in the map.
+ */
+int mmrangemap_pull(MMRangeMap* map,
+                    target_ulong start,
+                    target_ulong end,
+                    MMRangeDesc* pulled);
+
+/* Copies content of one memory map to another.
+ * Param:
+ *  to - Map where to copy entries to.
+ *  from - Map where to copy entries from.
+ * Return:
+ *  Zero on success, or -1 on error.
+ */
+int mmrangemap_copy(MMRangeMap* to, const MMRangeMap* from);
+
+/* Empties the map.
+ * Param:
+ *  map - Map to empty.
+ * Return:
+ *  Number of entries removed from the map.
+ */
+int mmrangemap_empty(MMRangeMap* map);
+
+#ifdef __cplusplus
+};  /* end of extern "C" */
+#endif
+
+#endif  // QEMU_MEMCHECK_MEMCHECK_MMRANGE_MAP_H
diff --git a/memcheck/memcheck_proc_management.c b/memcheck/memcheck_proc_management.c
new file mode 100644
index 0000000..531ec4a
--- /dev/null
+++ b/memcheck/memcheck_proc_management.c
@@ -0,0 +1,799 @@
+/* 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 routines related to process management in
+ * 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 "elff/elff_api.h"
+#include "memcheck.h"
+#include "memcheck_proc_management.h"
+#include "memcheck_logging.h"
+
+/* Current thread id.
+ * This value is updated with each call to memcheck_switch, saving here
+ * ID of the thread that becomes current. */
+static uint32_t current_tid = 0;
+
+/* Current thread descriptor.
+ * This variable is used to cache current thread descriptor. This value gets
+ * initialized on "as needed" basis, when descriptor for the current thread
+ * is requested for the first time.
+ * Note that every time memcheck_switch routine is called, this value gets
+ * NULL'ed, since another thread becomes current. */
+static ThreadDesc* current_thread = NULL;
+
+/* Current process descriptor.
+ * This variable is used to cache current process descriptor. This value gets
+ * initialized on "as needed" basis, when descriptor for the current process
+ * is requested for the first time.
+ * Note that every time memcheck_switch routine is called, this value gets
+ * NULL'ed, since new thread becomes current, thus process switch may have
+ * occurred as well. */
+static ProcDesc*    current_process = NULL;
+
+/* List of running processes. */
+static LIST_HEAD(proc_list, ProcDesc) proc_list;
+
+/* List of running threads. */
+static LIST_HEAD(thread_list, ThreadDesc) thread_list;
+
+// =============================================================================
+// Static routines
+// =============================================================================
+
+/* Creates and lists thread descriptor for a new thread.
+ * This routine will allocate and initialize new thread descriptor. After that
+ * this routine will insert the descriptor into the global list of running
+ * threads, as well as thread list in the process descriptor of the process
+ * in context of which this thread is created.
+ * Param:
+ *  proc - Process descriptor of the process, in context of which new thread
+ *      is created.
+ *  tid - Thread ID of the thread that's being created.
+ * Return:
+ *  New thread descriptor on success, or NULL on failure.
+ */
+static ThreadDesc*
+create_new_thread(ProcDesc* proc, uint32_t tid)
+{
+    ThreadDesc* new_thread = (ThreadDesc*)qemu_malloc(sizeof(ThreadDesc));
+    if (new_thread == NULL) {
+        ME("memcheck: Unable to allocate new thread descriptor.");
+        return NULL;
+    }
+    new_thread->tid = tid;
+    new_thread->process = proc;
+    new_thread->call_stack = NULL;
+    new_thread->call_stack_count = 0;
+    new_thread->call_stack_max = 0;
+    LIST_INSERT_HEAD(&thread_list, new_thread, global_entry);
+    LIST_INSERT_HEAD(&proc->threads, new_thread, proc_entry);
+    return new_thread;
+}
+
+/* Creates and lists process descriptor for a new process.
+ * This routine will allocate and initialize new process descriptor. After that
+ * this routine will create main thread descriptor for the process (with the
+ * thread ID equal to the new process ID), and then new process descriptor will
+ * be inserted into the global list of running processes.
+ * Param:
+ *  pid - Process ID of the process that's being created.
+ *  parent_pid - Process ID of the parent process.
+ * Return:
+ *  New process descriptor on success, or NULL on failure.
+ */
+static ProcDesc*
+create_new_process(uint32_t pid, uint32_t parent_pid)
+{
+    // Create and init new process descriptor.
+    ProcDesc* new_proc = (ProcDesc*)qemu_malloc(sizeof(ProcDesc));
+    if (new_proc == NULL) {
+        ME("memcheck: Unable to allocate new process descriptor");
+        return NULL;
+    }
+    LIST_INIT(&new_proc->threads);
+    allocmap_init(&new_proc->alloc_map);
+    mmrangemap_init(&new_proc->mmrange_map);
+    new_proc->pid = pid;
+    new_proc->parent_pid = parent_pid;
+    new_proc->image_path = NULL;
+    new_proc->flags = 0;
+
+    if (parent_pid != 0) {
+        /* If new process has been forked, it inherits a copy of parent's
+         * process heap, as well as parent's mmaping of loaded modules. So, on
+         * fork we're required to copy parent's allocation descriptors map, as
+         * well as parent's mmapping map to the new process. */
+        int failed;
+        ProcDesc* parent = get_process_from_pid(parent_pid);
+        if (parent == NULL) {
+            ME("memcheck: Unable to get parent process pid=%u for new process pid=%u",
+               parent_pid, pid);
+            qemu_free(new_proc);
+            return NULL;
+        }
+
+        /* Copy parent's allocation map, setting "inherited" flag, and clearing
+         * parent's "transition" flag in the copied entries. */
+        failed = allocmap_copy(&new_proc->alloc_map, &parent->alloc_map,
+                               MDESC_FLAG_INHERITED_ON_FORK,
+                               MDESC_FLAG_TRANSITION_ENTRY);
+        if (failed) {
+            ME("memcheck: Unable to copy process' %s[pid=%u] allocation map to new process pid=%u",
+               parent->image_path, parent_pid, pid);
+            allocmap_empty(&new_proc->alloc_map);
+            qemu_free(new_proc);
+            return NULL;
+        }
+
+        // Copy parent's memory mappings map.
+        failed = mmrangemap_copy(&new_proc->mmrange_map, &parent->mmrange_map);
+        if (failed) {
+            ME("memcheck: Unable to copy process' %s[pid=%u] mmrange map to new process pid=%u",
+               parent->image_path, parent_pid, pid);
+            mmrangemap_empty(&new_proc->mmrange_map);
+            allocmap_empty(&new_proc->alloc_map);
+            qemu_free(new_proc);
+            return NULL;
+        }
+    }
+
+    // Create and register main thread descriptor for new process.
+    if(create_new_thread(new_proc, pid) == NULL) {
+        mmrangemap_empty(&new_proc->mmrange_map);
+        allocmap_empty(&new_proc->alloc_map);
+        qemu_free(new_proc);
+        return NULL;
+    }
+
+    // List new process.
+    LIST_INSERT_HEAD(&proc_list, new_proc, global_entry);
+
+    return new_proc;
+}
+
+/* Finds thread descriptor for a thread id in the global list of running
+ * threads.
+ * Param:
+ *  tid - Thread ID to look up thread descriptor for.
+ * Return:
+ *  Found thread descriptor, or NULL if thread descriptor has not been found.
+ */
+static ThreadDesc*
+get_thread_from_tid(uint32_t tid)
+{
+    ThreadDesc* thread;
+
+    /* There is a pretty good chance that when this call is made, it's made
+     * to get descriptor for the current thread. Lets see if it is so, so
+     * we don't have to iterate through the entire list. */
+    if (tid == current_tid && current_thread != NULL) {
+        return current_thread;
+    }
+
+    LIST_FOREACH(thread, &thread_list, global_entry) {
+        if (tid == thread->tid) {
+            if (tid == current_tid) {
+                current_thread = thread;
+            }
+            return thread;
+        }
+    }
+    return NULL;
+}
+
+/* Gets thread descriptor for the current thread.
+ * Return:
+ *  Found thread descriptor, or NULL if thread descriptor has not been found.
+ */
+ThreadDesc*
+get_current_thread(void)
+{
+    // Lets see if current thread descriptor has been cached.
+    if (current_thread == NULL) {
+        /* Descriptor is not cached. Look it up in the list. Note that
+         * get_thread_from_tid(current_tid) is not used here in order to
+         * optimize this code for performance, as this routine is called from
+         * the performance sensitive path. */
+        ThreadDesc* thread;
+        LIST_FOREACH(thread, &thread_list, global_entry) {
+            if (current_tid == thread->tid) {
+                current_thread = thread;
+                return current_thread;
+            }
+        }
+    }
+    return current_thread;
+}
+
+/* Finds process descriptor for a thread id.
+ * Param:
+ *  tid - Thread ID to look up process descriptor for.
+ * Return:
+ *  Process descriptor for the thread, or NULL, if process descriptor
+ *  has not been found.
+ */
+static inline ProcDesc*
+get_process_from_tid(uint32_t tid)
+{
+    const ThreadDesc* thread = get_thread_from_tid(tid);
+    return (thread != NULL) ? thread->process : NULL;
+}
+
+/* Sets, or replaces process image path in process descriptor.
+ * Generally, new process' image path is unknown untill we calculate it in
+ * the handler for TRACE_DEV_REG_CMDLINE event. This routine is called from
+ * TRACE_DEV_REG_CMDLINE event handler to set, or replace process image path.
+ * Param:
+ *  proc - Descriptor of the process where to set, or replace image path.
+ *  image_path - Image path to the process, transmitted with
+ *      TRACE_DEV_REG_CMDLINE event.
+ * set_flags_on_replace - Flags to be set when current image path for the
+ *      process has been actually replaced with the new one.
+ * Return:
+ *  Zero on success, or -1 on failure.
+ */
+static int
+procdesc_set_image_path(ProcDesc* proc,
+                        const char* image_path,
+                        uint32_t set_flags_on_replace)
+{
+    if (image_path == NULL || proc == NULL) {
+        return 0;
+    }
+
+    if (proc->image_path != NULL) {
+        /* Process could have been forked, and inherited image path of the
+         * parent process. However, it seems that "fork" in terms of TRACE_XXX
+         * is not necessarly a strict "fork", but rather new process creation
+         * in general. So, if that's the case we need to override image path
+         * inherited from the parent process. */
+        if (!strcmp(proc->image_path, image_path)) {
+            // Paths are the same. Just bail out.
+            return 0;
+        }
+        qemu_free(proc->image_path);
+        proc->image_path = NULL;
+    }
+
+    // Save new image path into process' descriptor.
+    proc->image_path = qemu_malloc(strlen(image_path) + 1);
+    if (proc->image_path == NULL) {
+        ME("memcheck: Unable to allocate %u bytes for image path %s to set it for pid=%u",
+           strlen(image_path) + 1, image_path, proc->pid);
+        return -1;
+    }
+    strcpy(proc->image_path, image_path);
+    proc->flags |= set_flags_on_replace;
+    return 0;
+}
+
+/* Frees thread descriptor. */
+static void
+threaddesc_free(ThreadDesc* thread)
+{
+    uint32_t indx;
+
+    if (thread == NULL) {
+        return;
+    }
+
+    if (thread->call_stack != NULL) {
+        for (indx = 0; indx < thread->call_stack_count; indx++) {
+            if (thread->call_stack[indx].module_path != NULL) {
+                qemu_free(thread->call_stack[indx].module_path);
+            }
+        }
+        qemu_free(thread->call_stack);
+    }
+    qemu_free(thread);
+}
+
+// =============================================================================
+// Process management API
+// =============================================================================
+
+void
+memcheck_init_proc_management(void)
+{
+    LIST_INIT(&proc_list);
+    LIST_INIT(&thread_list);
+}
+
+ProcDesc*
+get_process_from_pid(uint32_t pid)
+{
+    ProcDesc* proc;
+
+    /* Chances are that pid addresses the current process. Lets check this,
+     * so we don't have to iterate through the entire project list. */
+    if (current_thread != NULL && current_thread->process->pid == pid) {
+        current_process = current_thread->process;
+        return current_process;
+    }
+
+    LIST_FOREACH(proc, &proc_list, global_entry) {
+        if (pid == proc->pid) {
+            break;
+        }
+    }
+    return proc;
+}
+
+ProcDesc*
+get_current_process(void)
+{
+    if (current_process == NULL) {
+        const ThreadDesc* cur_thread = get_current_thread();
+        if (cur_thread != NULL) {
+            current_process = cur_thread->process;
+        }
+    }
+    return current_process;
+}
+
+void
+memcheck_on_call(target_ulong from, target_ulong ret)
+{
+    const uint32_t grow_by = 32;
+    const uint32_t max_stack = grow_by;
+    ThreadDesc* thread = get_current_thread();
+    if (thread == NULL) {
+        return;
+    }
+
+    /* We're not saving call stack until process starts execution. */
+    if (!procdesc_is_executing(thread->process)) {
+        return;
+    }
+
+    const MMRangeDesc* rdesc = procdesc_get_range_desc(thread->process, from);
+    if (rdesc == NULL) {
+        ME("memcheck: Unable to find mapping for guest PC 0x%08X in process %s[pid=%u]",
+           from, thread->process->image_path, thread->process->pid);
+        return;
+    }
+
+    /* Limit calling stack size. There are cases when calling stack can be
+     * quite deep due to recursion (up to 4000 entries). */
+    if (thread->call_stack_count >= max_stack) {
+#if 0
+        /* This happens quite often. */
+        MD("memcheck: Thread stack for %s[pid=%u, tid=%u] is too big: %u",
+           thread->process->image_path, thread->process->pid, thread->tid,
+           thread->call_stack_count);
+#endif
+        return;
+    }
+
+    if (thread->call_stack_count >= thread->call_stack_max) {
+        /* Expand calling stack array buffer. */
+        thread->call_stack_max += grow_by;
+        ThreadCallStackEntry* new_array =
+            qemu_malloc(thread->call_stack_max * sizeof(ThreadCallStackEntry));
+        if (new_array == NULL) {
+            ME("memcheck: Unable to allocate %u bytes for calling stack.",
+               thread->call_stack_max * sizeof(ThreadCallStackEntry));
+            thread->call_stack_max -= grow_by;
+            return;
+        }
+        if (thread->call_stack_count != 0) {
+            memcpy(new_array, thread->call_stack,
+                   thread->call_stack_count * sizeof(ThreadCallStackEntry));
+        }
+        if (thread->call_stack != NULL) {
+            qemu_free(thread->call_stack);
+        }
+        thread->call_stack = new_array;
+    }
+    thread->call_stack[thread->call_stack_count].call_address = from;
+    thread->call_stack[thread->call_stack_count].call_address_rel =
+            mmrangedesc_get_module_offset(rdesc, from);
+    thread->call_stack[thread->call_stack_count].ret_address = ret;
+    thread->call_stack[thread->call_stack_count].ret_address_rel =
+            mmrangedesc_get_module_offset(rdesc, ret);
+    thread->call_stack[thread->call_stack_count].module_path =
+            qemu_malloc(strlen(rdesc->path) + 1);
+    if (thread->call_stack[thread->call_stack_count].module_path == NULL) {
+        ME("memcheck: Unable to allocate %u bytes for module path in the thread calling stack.",
+            strlen(rdesc->path) + 1);
+        return;
+    }
+    strcpy(thread->call_stack[thread->call_stack_count].module_path,
+           rdesc->path);
+    thread->call_stack_count++;
+}
+
+void
+memcheck_on_ret(target_ulong ret)
+{
+    ThreadDesc* thread = get_current_thread();
+    if (thread == NULL) {
+        return;
+    }
+
+    /* We're not saving call stack until process starts execution. */
+    if (!procdesc_is_executing(thread->process)) {
+        return;
+    }
+
+    if (thread->call_stack_count > 0) {
+        int indx = (int)thread->call_stack_count - 1;
+        for (; indx >= 0; indx--) {
+            if (thread->call_stack[indx].ret_address == ret) {
+                thread->call_stack_count = indx;
+                return;
+            }
+        }
+    }
+}
+
+// =============================================================================
+// Handlers for events, generated by the kernel.
+// =============================================================================
+
+void
+memcheck_init_pid(uint32_t new_pid)
+{
+    create_new_process(new_pid, 0);
+    T(PROC_NEW_PID, "memcheck: init_pid(pid=%u) in current thread tid=%u\n",
+      new_pid, current_tid);
+}
+
+void
+memcheck_switch(uint32_t tid)
+{
+    /* Since new thread became active, we have to invalidate cached
+     * descriptors for current thread and process. */
+    current_thread = NULL;
+    current_process = NULL;
+    current_tid = tid;
+}
+
+void
+memcheck_fork(uint32_t tgid, uint32_t new_pid)
+{
+    ProcDesc* parent_proc;
+    ProcDesc* new_proc;
+
+    /* tgid may match new_pid, in which case current process is the
+     * one that's being forked, otherwise tgid identifies process
+     * that's being forked. */
+    if (new_pid == tgid) {
+        parent_proc = get_current_process();
+    } else {
+        parent_proc = get_process_from_tid(tgid);
+    }
+
+    if (parent_proc == NULL) {
+        ME("memcheck: FORK(%u, %u): Unable to look up parent process. Current tid=%u",
+           tgid, new_pid, current_tid);
+        return;
+    }
+
+    if (parent_proc->pid != get_current_process()->pid) {
+        MD("memcheck: FORK(%u, %u): parent %s[pid=%u] is not the current process %s[pid=%u]",
+           tgid, new_pid, parent_proc->image_path, parent_proc->pid,
+           get_current_process()->image_path, get_current_process()->pid);
+    }
+
+    new_proc = create_new_process(new_pid, parent_proc->pid);
+    if (new_proc == NULL) {
+        return;
+    }
+
+    /* Since we're possibly forking parent process, we need to inherit
+     * parent's image path in the forked process. */
+    procdesc_set_image_path(new_proc, parent_proc->image_path, 0);
+
+    T(PROC_FORK, "memcheck: FORK(tgid=%u, new_pid=%u) by %s[pid=%u] (tid=%u)\n",
+      tgid, new_pid, parent_proc->image_path, parent_proc->pid, current_tid);
+}
+
+void
+memcheck_clone(uint32_t tgid, uint32_t new_tid)
+{
+    ProcDesc* parent_proc;
+
+    /* tgid may match new_pid, in which case current process is the
+     * one that creates thread, otherwise tgid identifies process
+     * that creates thread. */
+    if (new_tid == tgid) {
+        parent_proc = get_current_process();
+    } else {
+        parent_proc = get_process_from_tid(tgid);
+    }
+
+    if (parent_proc == NULL) {
+        ME("memcheck: CLONE(%u, %u) Unable to look up parent process. Current tid=%u",
+           tgid, new_tid, current_tid);
+        return;
+    }
+
+    if (parent_proc->pid != get_current_process()->pid) {
+        ME("memcheck: CLONE(%u, %u): parent %s[pid=%u] is not the current process %s[pid=%u]",
+           tgid, new_tid, parent_proc->image_path, parent_proc->pid,
+           get_current_process()->image_path, get_current_process()->pid);
+    }
+
+    create_new_thread(parent_proc, new_tid);
+
+    T(PROC_CLONE, "memcheck: CLONE(tgid=%u, new_tid=%u) by %s[pid=%u] (tid=%u)\n",
+      tgid, new_tid, parent_proc->image_path, parent_proc->pid, current_tid);
+}
+
+void
+memcheck_set_cmd_line(const char* cmd_arg, unsigned cmdlen)
+{
+    char parsed[4096];
+    int n;
+
+    ProcDesc* current_proc = get_current_process();
+    if (current_proc == NULL) {
+        ME("memcheck: CMDL(%s, %u): Unable to look up process for current tid=%3u",
+           cmd_arg, cmdlen, current_tid);
+        return;
+    }
+
+    /* Image path is the first agrument in cmd line. Note that due to
+     * limitations of TRACE_XXX cmdlen can never exceed CLIENT_PAGE_SIZE */
+    memcpy(parsed, cmd_arg, cmdlen);
+
+    // Cut first argument off the entire command line.
+    for (n = 0; n < cmdlen; n++) {
+        if (parsed[n] == ' ') {
+            break;
+        }
+    }
+    parsed[n] = '\0';
+
+    // Save process' image path into descriptor.
+    procdesc_set_image_path(current_proc, parsed,
+                            PROC_FLAG_IMAGE_PATH_REPLACED);
+    current_proc->flags |= PROC_FLAG_EXECUTING;
+
+    /* At this point we need to discard memory mappings inherited from
+     * the parent process, since this process has become "independent" from
+     * its parent. */
+    mmrangemap_empty(&current_proc->mmrange_map);
+    T(PROC_START, "memcheck: Executing process %s[pid=%u]\n",
+      current_proc->image_path, current_proc->pid);
+}
+
+void
+memcheck_exit(uint32_t exit_code)
+{
+    ProcDesc* proc;
+    int leaks_reported = 0;
+    MallocDescEx leaked_alloc;
+
+    // Exiting thread descriptor.
+    ThreadDesc* thread = get_current_thread();
+    if (thread == NULL) {
+        ME("memcheck: EXIT(%u): Unable to look up thread for current tid=%u",
+           exit_code, current_tid);
+        return;
+    }
+    proc = thread->process;
+
+    // Since current thread is exiting, we need to NULL its cached descriptor.
+    current_thread = NULL;
+
+    // Unlist the thread from its process as well as global lists.
+    LIST_REMOVE(thread, proc_entry);
+    LIST_REMOVE(thread, global_entry);
+    threaddesc_free(thread);
+
+    /* Lets see if this was last process thread, which would indicate
+     * process termination. */
+    if (!LIST_EMPTY(&proc->threads)) {
+        return;
+    }
+
+    // Process is terminating. Report leaks and free resources.
+    proc->flags |= PROC_FLAG_EXITING;
+
+    /* Empty allocation descriptors map for the exiting process,
+     * reporting leaking blocks in the process. */
+    while (!allocmap_pull_first(&proc->alloc_map, &leaked_alloc)) {
+        /* We should "forgive" blocks that were inherited from the
+         * parent process on fork, or were allocated while process was
+         * in "transition" state. */
+        if (!mallocdescex_is_inherited_on_fork(&leaked_alloc) &&
+            !mallocdescex_is_transition_entry(&leaked_alloc)) {
+            if (!leaks_reported) {
+                // First leak detected. Print report's header.
+                T(CHECK_LEAK, "memcheck: Process %s[pid=%u] is exiting leaking allocated blocks:\n",
+                  proc->image_path, proc->pid);
+            }
+            if (trace_flags & TRACE_CHECK_LEAK_ENABLED) {
+                // Dump leaked block information.
+                printf("   Leaked block %u:\n", leaks_reported + 1);
+                memcheck_dump_malloc_desc(&leaked_alloc, 0, 0);
+                if (leaked_alloc.call_stack != NULL) {
+                    const int max_stack = 24;
+                    if (max_stack >= leaked_alloc.call_stack_count) {
+                        printf("      Call stack:\n");
+                    } else {
+                        printf("      Call stack (first %u of %u entries):\n",
+                               max_stack, leaked_alloc.call_stack_count);
+                    }
+                    uint32_t stk;
+                    for (stk = 0;
+                         stk < leaked_alloc.call_stack_count && stk < max_stack;
+                         stk++) {
+                        const MMRangeDesc* rdesc =
+                           procdesc_find_mapentry(proc,
+                                                  leaked_alloc.call_stack[stk]);
+                        if (rdesc != NULL) {
+                            Elf_AddressInfo elff_info;
+                            ELFF_HANDLE elff_handle = NULL;
+                            uint32_t rel =
+                                mmrangedesc_get_module_offset(rdesc,
+                                                  leaked_alloc.call_stack[stk]);
+                            printf("         Frame %u: PC=0x%08X (relative 0x%08X) in module %s\n",
+                                   stk, leaked_alloc.call_stack[stk], rel,
+                                   rdesc->path);
+                            if (memcheck_get_address_info(leaked_alloc.call_stack[stk],
+                                                          rdesc, &elff_info,
+                                                          &elff_handle) == 0) {
+                                printf("            Routine %s @ %s/%s:%u\n",
+                                       elff_info.routine_name,
+                                       elff_info.dir_name,
+                                       elff_info.file_name,
+                                       elff_info.line_number);
+                                elff_free_pc_address_info(elff_handle,
+                                                          &elff_info);
+                                elff_close(elff_handle);
+                            }
+                        } else {
+                            printf("         Frame %u: PC=0x%08X in module <unknown>\n",
+                                   stk, leaked_alloc.call_stack[stk]);
+
+                        }
+                    }
+                }
+            }
+            leaks_reported++;
+        }
+    }
+
+    if (leaks_reported) {
+        T(CHECK_LEAK, "memcheck: Process %s[pid=%u] is leaking %u allocated blocks.\n",
+          proc->image_path, proc->pid, leaks_reported);
+    }
+
+    T(PROC_EXIT, "memcheck: Exiting process %s[pid=%u] in thread %u. Memory leaks detected: %u\n",
+      proc->image_path, proc->pid, current_tid, leaks_reported);
+
+    /* Since current process is exiting, we need to NULL its cached descriptor,
+     * and unlist it from the list of running processes. */
+    current_process = NULL;
+    LIST_REMOVE(proc, global_entry);
+
+    // Empty process' mmapings map.
+    mmrangemap_empty(&proc->mmrange_map);
+    if (proc->image_path != NULL) {
+        qemu_free(proc->image_path);
+    }
+    qemu_free(proc);
+}
+
+void
+memcheck_mmap_exepath(target_ulong vstart,
+                      target_ulong vend,
+                      target_ulong exec_offset,
+                      const char* path)
+{
+    MMRangeDesc desc;
+    MMRangeDesc replaced;
+    RBTMapResult ins_res;
+
+    ProcDesc* proc = get_current_process();
+    if (proc == NULL) {
+        ME("memcheck: MMAP(0x%08X, 0x%08X, 0x%08X, %s) Unable to look up current process. Current tid=%u",
+           vstart, vend, exec_offset, path, current_tid);
+        return;
+    }
+
+    /* First, unmap an overlapped section */
+    memcheck_unmap(vstart, vend);
+
+    /* Add new mapping. */
+    desc.map_start = vstart;
+    desc.map_end = vend;
+    desc.exec_offset = exec_offset;
+    desc.path = qemu_malloc(strlen(path) + 1);
+    if (desc.path == NULL) {
+        ME("memcheck: MMAP(0x%08X, 0x%08X, 0x%08X, %s) Unable to allocate path for the entry.",
+           vstart, vend, exec_offset, path);
+        return;
+    }
+    strcpy(desc.path, path);
+
+    ins_res = mmrangemap_insert(&proc->mmrange_map, &desc, &replaced);
+    if (ins_res == RBT_MAP_RESULT_ERROR) {
+        ME("memcheck: %s[pid=%u] unable to insert memory mapping entry: 0x%08X - 0x%08X",
+           proc->image_path, proc->pid, vstart, vend);
+        qemu_free(desc.path);
+        return;
+    }
+
+    if (ins_res == RBT_MAP_RESULT_ENTRY_REPLACED) {
+        MD("memcheck: %s[pid=%u] MMRANGE %s[0x%08X - 0x%08X] is replaced with %s[0x%08X - 0x%08X]",
+           proc->image_path, proc->pid, replaced.path, replaced.map_start,
+           replaced.map_end, desc.path, desc.map_start, desc.map_end);
+        qemu_free(replaced.path);
+    }
+
+    T(PROC_MMAP, "memcheck: %s[pid=%u] %s is mapped: 0x%08X - 0x%08X + 0x%08X\n",
+      proc->image_path, proc->pid, path, vstart, vend, exec_offset);
+}
+
+void
+memcheck_unmap(target_ulong vstart, target_ulong vend)
+{
+    MMRangeDesc desc;
+    ProcDesc* proc = get_current_process();
+    if (proc == NULL) {
+        ME("memcheck: UNMAP(0x%08X, 0x%08X) Unable to look up current process. Current tid=%u",
+           vstart, vend, current_tid);
+        return;
+    }
+
+    if (mmrangemap_pull(&proc->mmrange_map, vstart, vend, &desc)) {
+        return;
+    }
+
+    if (desc.map_start >= vstart && desc.map_end <= vend) {
+        /* Entire mapping has been deleted. */
+        T(PROC_MMAP, "memcheck: %s[pid=%u] %s is unmapped: [0x%08X - 0x%08X + 0x%08X]\n",
+          proc->image_path, proc->pid, desc.path, vstart, vend, desc.exec_offset);
+        qemu_free(desc.path);
+        return;
+    }
+
+    /* This can be first stage of "remap" request, when part of the existing
+     * mapping has been unmapped. If that's so, lets cut unmapped part from the
+     * block that we just pulled, and add whatever's left back to the map. */
+    T(PROC_MMAP, "memcheck: REMAP(0x%08X, 0x%08X + 0x%08X) -> (0x%08X, 0x%08X)\n",
+       desc.map_start, desc.map_end, desc.exec_offset, vstart, vend);
+    if (desc.map_start == vstart) {
+        /* We cut part from the beginning. Add the tail back. */
+        desc.exec_offset += vend - desc.map_start;
+        desc.map_start = vend;
+        mmrangemap_insert(&proc->mmrange_map, &desc, NULL);
+    } else if (desc.map_end == vend) {
+        /* We cut part from the tail. Add the beginning back. */
+        desc.map_end = vstart;
+        mmrangemap_insert(&proc->mmrange_map, &desc, NULL);
+    } else {
+        /* We cut piece in the middle. */
+        MMRangeDesc tail;
+        tail.map_start = vend;
+        tail.map_end = desc.map_end;
+        tail.exec_offset = vend - desc.map_start + desc.exec_offset;
+        tail.path = qemu_malloc(strlen(desc.path) + 1);
+        strcpy(tail.path, desc.path);
+        mmrangemap_insert(&proc->mmrange_map, &tail, NULL);
+        desc.map_end = vstart;
+        mmrangemap_insert(&proc->mmrange_map, &desc, NULL);
+    }
+}
diff --git a/memcheck/memcheck_proc_management.h b/memcheck/memcheck_proc_management.h
new file mode 100644
index 0000000..d5525b1
--- /dev/null
+++ b/memcheck/memcheck_proc_management.h
@@ -0,0 +1,327 @@
+/* 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 declarations of structures, routines, etc. related to process
+ * management in memchecker framework.
+ */
+
+#ifndef QEMU_MEMCHECK_MEMCHECK_PROC_MANAGEMENT_H
+#define QEMU_MEMCHECK_MEMCHECK_PROC_MANAGEMENT_H
+
+/* 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 "sys-queue.h"
+#include "memcheck_common.h"
+#include "memcheck_malloc_map.h"
+#include "memcheck_mmrange_map.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// =============================================================================
+// Process management structures
+// =============================================================================
+
+/* Describes a process that is monitored by memchecker framework. */
+typedef struct ProcDesc {
+    /* Map of memory blocks allocated in context of this process. */
+    AllocMap                                    alloc_map;
+
+    /* Map of memory mapped modules loaded in context of this process. */
+    MMRangeMap                                  mmrange_map;
+
+    /* Descriptor's entry in the global process list. */
+    LIST_ENTRY(ProcDesc)                        global_entry;
+
+    /* List of threads running in context of this process. */
+    LIST_HEAD(threads, ThreadDesc)              threads;
+
+    /* Path to the process' image file. */
+    char*                                       image_path;
+
+    /* Process id. */
+    uint32_t                                    pid;
+
+    /* Parent process id. */
+    uint32_t                                    parent_pid;
+
+    /* Misc. process flags. See PROC_FLAG_XXX */
+    uint32_t                                    flags;
+} ProcDesc;
+
+/* Process is executing. */
+#define PROC_FLAG_EXECUTING             0x00000001
+/* Process is exiting. */
+#define PROC_FLAG_EXITING               0x00000002
+/* ProcDesc->image_path has been replaced during process execution. */
+#define PROC_FLAG_IMAGE_PATH_REPLACED   0x00000004
+/* libc.so instance has been initialized for this process. */
+#define PROC_FLAG_LIBC_INITIALIZED      0x00000008
+
+/* Entry in the thread's calling stack array. */
+typedef struct ThreadCallStackEntry {
+    /* Guest PC where call has been made. */
+    target_ulong    call_address;
+    /* Guest PC where call has been made, relative to the beginning of the
+     * mapped module that contains call_address. */
+    target_ulong    call_address_rel;
+    /* Guest PC where call will return. */
+    target_ulong    ret_address;
+    /* Guest PC where call will return, relative to the beginning of the
+     * mapped module that contains ret_address. */
+    target_ulong    ret_address_rel;
+    /* Path to the image file of the module containing call_address. */
+    char*           module_path;
+} ThreadCallStackEntry;
+
+/* Describes a thread that is monitored by memchecker framework. */
+typedef struct ThreadDesc {
+    /* Descriptor's entry in the global thread list. */
+    LIST_ENTRY(ThreadDesc)  global_entry;
+
+    /* Descriptor's entry in the process' thread list. */
+    LIST_ENTRY(ThreadDesc)  proc_entry;
+
+    /* Descriptor of the process this thread belongs to. */
+    ProcDesc*               process;
+
+    /* Calling stack for this thread. */
+    ThreadCallStackEntry*   call_stack;
+
+    /* Number of entries in the call_stack array. */
+    uint32_t                call_stack_count;
+
+    /* Maximum number of entries that can fit into call_stack buffer. */
+    uint32_t                call_stack_max;
+
+    /* Thread id. */
+    uint32_t                tid;
+} ThreadDesc;
+
+// =============================================================================
+// Inlines
+// =============================================================================
+
+/* Checks if process has been forked, rather than created from a "fresh" PID.
+ * Param:
+ *  proc - Descriptor for the process to check.
+ * Return:
+ *  boolean: 1 if process has been forked, or 0 if it was
+ *  created from a "fresh" PID.
+ */
+static inline int
+procdesc_is_forked(const ProcDesc* proc)
+{
+    return proc->parent_pid != 0;
+}
+
+/* Checks if process is executing.
+ * Param:
+ *  proc - Descriptor for the process to check.
+ * Return:
+ *  boolean: 1 if process is executing, or 0 if it is not executing.
+ */
+static inline int
+procdesc_is_executing(const ProcDesc* proc)
+{
+    return (proc->flags & PROC_FLAG_EXECUTING) != 0;
+}
+
+/* Checks if process is exiting.
+ * Param:
+ *  proc - Descriptor for the process to check.
+ * Return:
+ *  boolean: 1 if process is exiting, or 0 if it is still alive.
+ */
+static inline int
+procdesc_is_exiting(const ProcDesc* proc)
+{
+    return (proc->flags & PROC_FLAG_EXITING) != 0;
+}
+
+/* Checks if process has initialized its libc.so instance.
+ * Param:
+ *  proc - Descriptor for the process to check.
+ * Return:
+ *  boolean: 1 if process has initialized its libc.so instance, or 0 otherwise.
+ */
+static inline int
+procdesc_is_libc_initialized(const ProcDesc* proc)
+{
+    return (proc->flags & PROC_FLAG_LIBC_INITIALIZED) != 0;
+}
+
+/* Checks if process image path has been replaced.
+ * Param:
+ *  proc - Descriptor for the process to check.
+ * Return:
+ *  boolean: 1 if process image path has been replaced,
+ *  or 0 if it was not replaced.
+ */
+static inline int
+procdesc_is_image_path_replaced(const ProcDesc* proc)
+{
+    return (proc->flags & PROC_FLAG_IMAGE_PATH_REPLACED) != 0;
+}
+
+// =============================================================================
+// Process management API
+// =============================================================================
+
+/* Gets thread descriptor for the current thread.
+ * Return:
+ *  Found thread descriptor, or NULL if thread descriptor has not been found.
+ */
+ThreadDesc* get_current_thread(void);
+
+/* Initializes process management API. */
+void memcheck_init_proc_management(void);
+
+/* Gets process descriptor for the current process.
+ * Return:
+ *  Process descriptor for the current process, or NULL, if process descriptor
+ *  has not been found.
+ */
+ProcDesc* get_current_process(void);
+
+/* Finds process descriptor for a process id.
+ * Param:
+ *  pid - Process ID to look up process descriptor for.
+ * Return:
+ *  Process descriptor for the PID, or NULL, if process descriptor
+ *  has not been found.
+ */
+ProcDesc* get_process_from_pid(uint32_t pid);
+
+/* Inserts new (or replaces existing) entry in the allocation descriptors map
+ * for the given process.
+ * See allocmap_insert for more information on this routine, its parameters
+ * and returning value.
+ * Param:
+ *  proc - Process descriptor where to add new allocation entry info.
+ */
+static inline RBTMapResult
+procdesc_add_malloc(ProcDesc* proc,
+                    const MallocDescEx* desc,
+                    MallocDescEx* replaced)
+{
+    return allocmap_insert(&proc->alloc_map, desc, replaced);
+}
+
+/* Finds an entry in the allocation descriptors map for the given process,
+ * matching given address range.
+ * See allocmap_find for more information on this routine, its parameters
+ * and returning value.
+ * Param:
+ *  proc - Process descriptor where to find an allocation entry.
+ */
+static inline MallocDescEx*
+procdesc_find_malloc_for_range(ProcDesc* proc,
+                               target_ulong address,
+                               uint32_t block_size)
+{
+    return allocmap_find(&proc->alloc_map, address, block_size);
+}
+
+/* Finds an entry in the allocation descriptors map for the given process,
+ * matching given address.
+ * See allocmap_find for more information on this routine, its parameters
+ * and returning value.
+ * Param:
+ *  proc - Process descriptor where to find an allocation entry.
+ */
+static inline MallocDescEx*
+procdesc_find_malloc(ProcDesc* proc, target_ulong address)
+{
+    return procdesc_find_malloc_for_range(proc, address, 1);
+}
+
+/* Pulls (finds and removes) an entry from the allocation descriptors map for
+ * the given process, matching given address.
+ * See allocmap_pull for more information on this routine, its parameters
+ * and returning value.
+ * Param:
+ *  proc - Process descriptor where to pull an allocation entry from.
+ */
+static inline int
+procdesc_pull_malloc(ProcDesc* proc, target_ulong address, MallocDescEx* pulled)
+{
+    return allocmap_pull(&proc->alloc_map, address, pulled);
+}
+
+/* Empties allocation descriptors map for the process.
+ * Param:
+ *  proc - Process to empty allocation map for.
+ * Return:
+ *  Number of entries deleted from the allocation map.
+ */
+static inline int
+procdesc_empty_alloc_map(ProcDesc* proc)
+{
+    return allocmap_empty(&proc->alloc_map);
+}
+
+/* Finds mmapping entry for the given address in the given process.
+ * Param:
+ *  proc - Descriptor of the process where to look for an entry.
+ *  addr - Address in the guest space for which to find an entry.
+ * Return:
+ *  Mapped entry, or NULL if no mapping for teh given address exists in the
+ *  process address space.
+ */
+static inline MMRangeDesc*
+procdesc_find_mapentry(const ProcDesc* proc, target_ulong addr)
+{
+    return mmrangemap_find(&proc->mmrange_map, addr, addr + 1);
+}
+
+/* Gets module descriptor for the given address.
+ * Param:
+ *  proc - Descriptor of the process where to look for a module.
+ *  addr - Address in the guest space for which to find a module descriptor.
+ * Return:
+ *  module descriptor for the module containing the given address, or NULL if no
+ *  such module has been found in the process' map of mmaped modules.
+ */
+static inline const MMRangeDesc*
+procdesc_get_range_desc(const ProcDesc* proc, target_ulong addr)
+{
+    return procdesc_find_mapentry(proc, addr);
+}
+
+/* Gets name of the module mmaped in context of the given process for the
+ * given address.
+ * Param:
+ *  proc - Descriptor of the process where to look for a module.
+ *  addr - Address in the guest space for which to find a module.
+ * Return:
+ *  Image path to the module containing the given address, or NULL if no such
+ *  module has been found in the process' map of mmaped modules.
+ */
+static inline const char*
+procdesc_get_module_path(const ProcDesc* proc, target_ulong addr)
+{
+    MMRangeDesc* rdesc = procdesc_find_mapentry(proc, addr);
+    return rdesc != NULL ? rdesc->path : NULL;
+}
+
+#ifdef __cplusplus
+};  /* end of extern "C" */
+#endif
+
+#endif  // QEMU_MEMCHECK_MEMCHECK_PROC_MANAGEMENT_H
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;
+}
diff --git a/memcheck/memcheck_util.h b/memcheck/memcheck_util.h
new file mode 100644
index 0000000..d4f6c8a
--- /dev/null
+++ b/memcheck/memcheck_util.h
@@ -0,0 +1,245 @@
+/* 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 declarations of utility routines for memchecker framework.
+ */
+
+#ifndef QEMU_MEMCHECK_MEMCHECK_UTIL_H
+#define QEMU_MEMCHECK_MEMCHECK_UTIL_H
+
+/* 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 "memcheck_common.h"
+#include "elff_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// =============================================================================
+// Transfering data between guest and emulator address spaces.
+// =============================================================================
+
+/* Copies buffer residing in the guest's virtual address space to a buffer
+ * in the emulator's address space.
+ * Param:
+ *  guest_address - Address of the bufer in guest's virtual address space.
+ *  qemu_address - Address of the bufer in the emulator's address space.
+ *  buffer_size - Byte size of the guest's buffer.
+ */
+void memcheck_get_guest_buffer(void* qemu_address,
+                               target_ulong guest_address,
+                               size_t buffer_size);
+
+/* Copies buffer residing in the emulator's address space to a buffer in the
+ * guest's virtual address space.
+ * Param:
+ *  qemu_address - Address of the bufer in the emulator's address space.
+ *  guest_address - Address of the bufer in guest's virtual address space.
+ *  buffer_size - Byte size of the emualtor's buffer.
+ */
+void memcheck_set_guest_buffer(target_ulong guest_address,
+                               const void* qemu_address,
+                               size_t buffer_size);
+
+/* Copies zero-terminated string residing in the guest's virtual address space
+ * to a string buffer in emulator's address space.
+ * Param:
+ *  qemu_str - Address of the string bufer in the emulator's address space.
+ *  guest_str - Address of the string in guest's virtual address space.
+ *  qemu_buffer_size - Size of the emulator's string buffer.
+ * Return
+ *  Length of the string that has been copied.
+ */
+size_t memcheck_get_guest_string(char* qemu_str,
+                                 target_ulong guest_str,
+                                 size_t qemu_buffer_size);
+
+/* Copies zero-terminated string residing in the guest's kernel address space
+ * to a string buffer in emulator's address space.
+ * Param:
+ *  qemu_str - Address of the string bufer in the emulator's address space.
+ *  guest_str - Address of the string in guest's kernel address space.
+ *  qemu_buffer_size - Size of the emulator's string buffer.
+ * Return
+ *  Length of the string that has been copied.
+ */
+size_t memcheck_get_guest_kernel_string(char* qemu_str,
+                                        target_ulong guest_str,
+                                        size_t qemu_buffer_size);
+
+// =============================================================================
+// Helpers for transfering memory allocation information.
+// =============================================================================
+
+/* Copies memory allocation descriptor from the guest's address space to the
+ * emulator's memory.
+ * Param:
+ *  qemu_address - Descriptor address in the emulator's address space where to
+ *      copy descriptor.
+ *  guest_address - Descriptor address in the guest's address space.
+ */
+static inline void
+memcheck_get_malloc_descriptor(MallocDesc* qemu_address,
+                               target_ulong guest_address)
+{
+    memcheck_get_guest_buffer(qemu_address, guest_address, sizeof(MallocDesc));
+}
+
+/* Copies memory allocation descriptor from the emulator's memory to the guest's
+ * address space.
+ * Param:
+ *  guest_address - Descriptor address in the guest's address space.
+ *  qemu_address - Descriptor address in the emulator's address space where to
+ *  copy descriptor.
+ */
+static inline void
+memcheck_set_malloc_descriptor(target_ulong guest_address,
+                               const MallocDesc* qemu_address)
+{
+    memcheck_set_guest_buffer(guest_address, qemu_address, sizeof(MallocDesc));
+}
+
+/* Copies memory free descriptor from the guest's address space to the
+ * emulator's memory.
+ * Param:
+ *  qemu_address - Descriptor address in the emulator's address space where to
+ *      copy descriptor.
+ *  guest_address - Descriptor address in the guest's address space.
+ */
+static inline void
+memcheck_get_free_descriptor(MallocFree* qemu_address,
+                             target_ulong guest_address)
+{
+    memcheck_get_guest_buffer(qemu_address, guest_address, sizeof(MallocFree));
+}
+
+/* Copies memory allocation query descriptor from the guest's address space to
+ * the emulator's memory.
+ * Param:
+ *  guest_address - Descriptor address in the guest's address space.
+ *  qemu_address - Descriptor address in the emulator's address space where to
+ *      copy descriptor.
+ */
+static inline void
+memcheck_get_query_descriptor(MallocDescQuery* qemu_address,
+                              target_ulong guest_address)
+{
+    memcheck_get_guest_buffer(qemu_address, guest_address,
+                              sizeof(MallocDescQuery));
+}
+
+/* Fails allocation request (TRACE_DEV_REG_MALLOC event).
+ * Allocation request failure is reported by zeroing 'libc_pid' filed in the
+ * allocation descriptor in the guest's address space.
+ * Param:
+ *  guest_address - Allocation descriptor address in the guest's address space,
+ *      where to record failure.
+ */
+void memcheck_fail_alloc(target_ulong guest_address);
+
+/* Fails free request (TRACE_DEV_REG_FREE_PTR event).
+ * Free request failure is reported by zeroing 'libc_pid' filed in the free
+ * descriptor in the guest's address space.
+ * Param:
+ *  guest_address - Free descriptor address in the guest's address space, where
+ *      to record failure.
+ */
+void memcheck_fail_free(target_ulong guest_address);
+
+/* Fails memory allocation query request (TRACE_DEV_REG_QUERY_MALLOC event).
+ * Query request failure is reported by zeroing 'libc_pid' filed in the query
+ * descriptor in the guest's address space.
+ * Param:
+ *  guest_address - Query descriptor address in the guest's address space, where
+ *      to record failure.
+ */
+void memcheck_fail_query(target_ulong guest_address);
+
+// =============================================================================
+// Misc. utility routines.
+// =============================================================================
+
+/* Converts PC address in the translated block to a corresponded PC address in
+ * the guest address space.
+ * Param:
+ *  tb_pc - PC address in the translated block.
+ * Return:
+ *  Corresponded PC address in the guest address space on success, or NULL if
+ *  conversion has failed.
+ */
+static inline target_ulong
+memcheck_tpc_to_gpc(target_ulong tb_pc)
+{
+    const TranslationBlock* tb = tb_find_pc(tb_pc);
+    return tb != NULL ? tb_search_guest_pc_from_tb_pc(tb, tb_pc) : 0;
+}
+
+/* Invalidates TLB table pages that contain given memory range.
+ * This routine is called after new entry is inserted into allocation map, so
+ * every access to the allocated block will cause __ld/__stx_mmu to be called.
+ * Param:
+ *  start - Beginning of the allocated block to invalidate pages for.
+ *  end - End of (past one byte after) the allocated block to invalidate pages
+ *      for.
+ */
+void invalidate_tlb_cache(target_ulong start, target_ulong end);
+
+/* Gets routine, file path and line number information for a PC address in the
+ * given module.
+ * Param:
+ *  abs_pc - PC address.
+ *  rdesc - Mapped memory range descriptor for the module containing abs_pc.
+ *  info - Upon successful return will contain routine, file path and line
+ *      information for the given PC address in the given module.
+ *      NOTE: Pathnames, saved into this structure are contained in mapped
+ *      sections of the symbols file for the module addressed by module_path.
+ *      Thus, pathnames are accessible only while elff_handle returned from this
+ *      routine remains opened.
+ *      NOTE: each successful call to this routine requires the caller to call
+ *      elff_free_pc_address_info for Elf_AddressInfo structure.
+ *  elff_handle - Upon successful return will contain a handle to the ELFF API
+ *      that wraps symbols file for the module, addressed by module_path. The
+ *      handle must remain opened for as long as pathnames in the info structure
+ *      are accessed, and must be eventually closed via call to elff_close.
+ * Return:
+ *  0 on success, 1, if symbols file for the module has not been found, or -1 on
+ *  other failures. If a failure is returned from this routine content of info
+ *  and elff_handle parameters is undefined.
+ */
+int memcheck_get_address_info(target_ulong abs_pc,
+                              const MMRangeDesc* rdesc,
+                              Elf_AddressInfo* info,
+                              ELFF_HANDLE* elff_handle);
+
+/* Dumps content of an allocation descriptor to stdout.
+ * Param desc - Allocation descriptor to dump.
+ * print_flags - If 1, flags field of the descriptor will be dumped to stdout.
+ *      If 0, flags filed will not be dumped.
+ * print_proc_info - If 1, allocator's process information for the descriptor
+ *      will be dumped to stdout. If 0, allocator's process information will
+ *      not be dumped.
+ */
+void memcheck_dump_malloc_desc(const MallocDescEx* desc,
+                               int print_flags,
+                               int print_proc_info);
+
+#ifdef __cplusplus
+};  /* end of extern "C" */
+#endif
+
+#endif  // QEMU_MEMCHECK_MEMCHECK_UTIL_H