C++程序  |  1015行  |  39.08 KB

/*
 * Copyright (C) 2009 The Android Open Source Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Contains implementation of memory allocation routines instrumented for
 * usage in the emulator to detect memory allocation violations, such as
 * memory leaks, buffer overruns, etc.
 * Code, implemented here is intended to run in the emulated environment only,
 * and serves simply as hooks into memory allocation routines. Main job of this
 * code is to notify the emulator about memory being allocated/deallocated,
 * providing information about each allocation. The idea is that emulator will
 * keep list of currently allocated blocks, and, knowing boundaries of each
 * block it will be able to verify that ld/st access to these blocks don't step
 * over boundaries set for the user. To enforce that, each memory block
 * allocated by this code is guarded with "prefix" and "suffix" areas, so
 * every time emulator detects access to any of these guarding areas, it can be
 * considered as access violation.
 */

#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include "dlmalloc.h"
#include "logd.h"
#include "malloc_debug_common.h"

/* This file should be included into the build only when
 * MALLOC_QEMU_INSTRUMENT macro is defined. */
#ifndef MALLOC_QEMU_INSTRUMENT
#error MALLOC_QEMU_INSTRUMENT is not defined.
#endif  // !MALLOC_QEMU_INSTRUMENT

/* Controls access violation test performed to make sure that we catch AVs
 * all the time they occur. See test_access_violation for more info. This macro
 * is used for internal testing purposes and should always be set to zero for
 * the production builds. */
#define TEST_ACCESS_VIOLATIONS  0

// =============================================================================
// 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, except for
 * replacing pointers with target_ulong) is also declared in the emulator's
 * sources (file memcheck/memcheck_common.h). So, every time a change is made to
 * any of these two declaration, another one must be also updated accordingly.
 */
typedef struct MallocDesc {
    /* Pointer 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.
     */
    void*       ptr;

    /* Number 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;

/* 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, calloc, memalign, or realloc.
 * Since we don't keep this information directly 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, except for replacing pointers
 * with target_ulong) is also declared in the emulator's sources (file
 * memcheck/memecheck_common.h). 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.
     */
    void*       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 of memory allocation descriptor for the queried pointer.
     * Descriptor, addressed by this field is initialized by the emulator in
     * response to the query.
     */
    MallocDesc*  desc;
} MallocDescQuery;

/* 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, except for replacing pointers
 * with target_ulong) is also declared in the emulator's sources (file
 * memcheck/memecheck_common.h). 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. */
    void*       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;

// =============================================================================
// Communication events
// =============================================================================

/* 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 been allocated.
 * Event's value parameter points to MallocDesc instance 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 a failure emulator will zero that field 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 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 a failure emulator will zero that field before completing
 * this event.
 */
#define TRACE_DEV_REG_FREE_PTR              1538

/* Queries the emulator about allocated memory block information.
 * Event's value parameter points to MallocDescQuery descriptor 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 a failure
 * emulator will zero that field 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 a zero-terminated string to be printed.
 */
#define TRACE_DEV_REG_PRINT_USER_STR        1540

static void notify_qemu_string(const char* str);
static void qemu_log(int prio, const char* fmt, ...);
static void dump_malloc_descriptor(char* str,
                                   size_t str_buf_size,
                                   const MallocDesc* desc);

// =============================================================================
// Macros
// =============================================================================

/* Defines default size of allocation prefix.
 * Note that we make prefix area quite large in order to increase chances of
 * catching buffer overflow. */
#define DEFAULT_PREFIX_SIZE     (malloc_alignment * 4)

/* Defines default size of allocation suffix.
 * Note that we make suffix area quite large in order to increase chances of
 * catching buffer overflow. */
#define DEFAULT_SUFFIX_SIZE     (malloc_alignment * 4)

/* Debug tracing has been enabled by the emulator. */
#define DEBUG_TRACING_ENABLED   0x00000001
/* Error tracing has been enabled by the emulator. */
#define ERROR_TRACING_ENABLED   0x00000002
/* Info tracing has been enabled by the emulator. */
#define INFO_TRACING_ENABLED    0x00000004
/* All tracing flags combined. */
#define ALL_TRACING_ENABLED (DEBUG_TRACING_ENABLED |    \
                             ERROR_TRACING_ENABLED |    \
                             INFO_TRACING_ENABLED)

/* Prints a string to the emulator's stdout.
 * In early stages of system loading, logging mesages via
 * __libc_android_log_print API is not available, because ADB API has not been
 * hooked up yet. So, in order to see such messages we need to print them to
 * the emulator's stdout.
 * Parameters passed to this macro are the same as parameters for printf
 * routine.
 */
#define TR(...)                                         \
    do {                                                \
        char tr_str[4096];                              \
        snprintf(tr_str, sizeof(tr_str), __VA_ARGS__ ); \
        tr_str[sizeof(tr_str) - 1] = '\0';              \
        notify_qemu_string(&tr_str[0]);                 \
    } while (0)

// =============================================================================
// Logging macros. Note that we simultaneously log messages to ADB and emulator.
// =============================================================================

/*
 * Helper macros for checking if particular trace level is enabled.
 */
#define debug_LOG_ENABLED       ((tracing_flags & DEBUG_TRACING_ENABLED) != 0)
#define error_LOG_ENABLED       ((tracing_flags & ERROR_TRACING_ENABLED) != 0)
#define info_LOG_ENABLED        ((tracing_flags & INFO_TRACING_ENABLED)  != 0)
#define tracing_enabled(type)   (type##_LOG_ENABLED)

/*
 * Logging helper macros.
 */
#define debug_log(format, ...)                                              \
    do {                                                                    \
        __libc_android_log_print(ANDROID_LOG_DEBUG, "memcheck",             \
                                 (format), ##__VA_ARGS__ );                 \
        if (tracing_flags & DEBUG_TRACING_ENABLED) {                        \
            qemu_log(ANDROID_LOG_DEBUG, (format), ##__VA_ARGS__ );          \
        }                                                                   \
    } while (0)

#define error_log(format, ...)                                              \
    do {                                                                    \
        __libc_android_log_print(ANDROID_LOG_ERROR, "memcheck",             \
                                 (format), ##__VA_ARGS__ );                 \
        if (tracing_flags & ERROR_TRACING_ENABLED) {                        \
            qemu_log(ANDROID_LOG_ERROR, (format), ##__VA_ARGS__ );          \
        }                                                                   \
    } while (0)

#define info_log(format, ...)                                               \
    do {                                                                    \
        __libc_android_log_print(ANDROID_LOG_INFO, "memcheck",              \
                                 (format), ##__VA_ARGS__ );                 \
        if (tracing_flags & INFO_TRACING_ENABLED) {                         \
            qemu_log(ANDROID_LOG_INFO, (format), ##__VA_ARGS__ );           \
        }                                                                   \
    } while (0)

/* Logs message dumping MallocDesc instance at the end of the message.
 * Param:
 *  type - Message type: debug, error, or info
 *  desc - MallocDesc instance to dump.
 *  frmt + rest - Formats message preceding dumped descriptor.
*/
#define log_mdesc(type, desc, frmt, ...)                                    \
    do {                                                                    \
        if (tracing_enabled(type)) {                                        \
            char log_str[4096];                                             \
            size_t str_len;                                                 \
            snprintf(log_str, sizeof(log_str), frmt, ##__VA_ARGS__);        \
            log_str[sizeof(log_str) - 1] = '\0';                            \
            str_len = strlen(log_str);                                      \
            dump_malloc_descriptor(log_str + str_len,                       \
                                   sizeof(log_str) - str_len,               \
                                   (desc));                                 \
            type##_log(log_str);                                            \
        }                                                                   \
    } while (0)

// =============================================================================
// Static data
// =============================================================================

/* Emulator's magic page address.
 * This page (mapped on /dev/qemu_trace device) is used to fire up events
 * in the emulator. */
static volatile void* qtrace = NULL;

/* Cached PID of the process in context of which this libc instance
 * has been initialized. */
static uint32_t malloc_pid = 0;

/* Memory allocation alignment that is used in dlmalloc.
 * This variable is updated by memcheck_initialize routine. */
static uint32_t malloc_alignment = 8;

/* Tracing flags. These flags control which types of logging messages are
 * enabled by the emulator. See XXX_TRACING_ENABLED for the values of flags
 * stored in this variable. This variable is updated by memcheck_initialize
 * routine. */
static uint32_t tracing_flags = 0;

// =============================================================================
// Static routines
// =============================================================================

/* 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 void*
mallocdesc_user_ptr(const MallocDesc* desc)
{
    return (char*)desc->ptr + desc->prefix_size;
}

/* Gets size of memory block actually allocated from the heap for the given
 * allocation decriptor.
 * Param:
 *  desc - Allocation descriptor.
 * Return:
 *  Size of memory block actually allocated from the heap.
 */
static inline uint32_t
mallocdesc_alloc_size(const MallocDesc* desc)
{
    return desc->prefix_size + desc->requested_bytes + desc->suffix_size;
}

/* Gets pointer to 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 (one byte past) the allocated block.
 */
static inline void*
mallocdesc_alloc_end(const MallocDesc* desc)
{
    return (char*)desc->ptr + mallocdesc_alloc_size(desc);
}

/* Fires up an event in the emulator.
 * Param:
 *  code - Event code (one of the TRACE_DEV_XXX).
 *  val  - Event's value parameter.
 */
static inline void
notify_qemu(uint32_t code, uint32_t val)
{
    if (NULL != qtrace) {
        *(volatile uint32_t*)((uint32_t)qtrace + ((code - 1024) << 2)) = val;
    }
}

/* Prints a zero-terminated string to the emulator's stdout (fires up
 * TRACE_DEV_REG_PRINT_USER_STR event in the emulator).
 * Param:
 *  str - Zero-terminated string to print.
 */
static void
notify_qemu_string(const char* str)
{
    if (str != NULL) {
        notify_qemu(TRACE_DEV_REG_PRINT_USER_STR, (uint32_t)str);
    }
}

/* Fires up TRACE_DEV_REG_LIBC_INIT event in the emulator.
 * Param:
 *  pid - ID of the process that initialized libc.
 */
static void
notify_qemu_libc_initialized(uint32_t pid)
{
    notify_qemu(TRACE_DEV_REG_LIBC_INIT, pid);
}

/* Fires up TRACE_DEV_REG_MALLOC event in the emulator.
 * Param:
 *  desc - Pointer to MallocDesc instance containing allocated block
 *      information.
 * Return:
 *  Zero on success, or -1 on failure. Note that on failure libc_pid field of
 *  the desc parameter passed to this routine has been zeroed out by the
 *  emulator.
 */
static inline int
notify_qemu_malloc(volatile MallocDesc* desc)
{
    desc->libc_pid = malloc_pid;
    desc->allocator_pid = getpid();
    desc->av_count = 0;
    notify_qemu(TRACE_DEV_REG_MALLOC, (uint32_t)desc);

    /* Emulator reports failure by zeroing libc_pid field of the
     * descriptor. */
    return desc->libc_pid != 0 ? 0 : -1;
}

/* Fires up TRACE_DEV_REG_FREE_PTR event in the emulator.
 * Param:
 *  ptr - Pointer to the memory block that's being freed.
 * Return:
 *  Zero on success, or -1 on failure.
 */
static inline int
notify_qemu_free(void* ptr_to_free)
{
    volatile MallocFree free_desc;

    free_desc.ptr = ptr_to_free;
    free_desc.libc_pid = malloc_pid;
    free_desc.free_pid = getpid();
    notify_qemu(TRACE_DEV_REG_FREE_PTR, (uint32_t)&free_desc);

    /* Emulator reports failure by zeroing libc_pid field of the
     * descriptor. */
    return free_desc.libc_pid != 0 ? 0 : -1;
}

/* Fires up TRACE_DEV_REG_QUERY_MALLOC event in the emulator.
 * Param:
 *  ptr - Pointer to request allocation information for.
 *  desc - Pointer to MallocDesc instance that will receive allocation
 *      information.
 *  routine - Code of the allocation routine, in context of which query is made:
 *      1 - free
 *      2 - realloc
 * Return:
 *  Zero on success, or -1 on failure.
 */
static inline int
query_qemu_malloc_info(void* ptr, MallocDesc* desc, uint32_t routine)
{
    volatile MallocDescQuery query;

    query.ptr = ptr;
    query.libc_pid = malloc_pid;
    query.query_pid = getpid();
    query.routine = routine;
    query.desc = desc;
    notify_qemu(TRACE_DEV_REG_QUERY_MALLOC, (uint32_t)&query);

    /* Emulator reports failure by zeroing libc_pid field of the
     * descriptor. */
    return query.libc_pid != 0 ? 0 : -1;
}

/* Logs a message to emulator's stdout.
 * Param:
 *  prio - Message priority (debug, info, or error)
 *  fmt + rest - Message format and parameters.
 */
static void
qemu_log(int prio, const char* fmt, ...)
{
    va_list ap;
    char buf[4096];
    const char* prefix;

    /* Choose message prefix depending on the priority value. */
    switch (prio) {
        case ANDROID_LOG_ERROR:
            if (!tracing_enabled(error)) {
                return;
            }
            prefix = "E";
            break;
        case ANDROID_LOG_INFO:
            if (!tracing_enabled(info)) {
                return;
            }
            prefix = "I";
            break;
        case ANDROID_LOG_DEBUG:
        default:
            if (!tracing_enabled(debug)) {
                return;
            }
            prefix = "D";
            break;
    }

    va_start(ap, fmt);
    vsnprintf(buf, sizeof(buf), fmt, ap);
    va_end(ap);
    buf[sizeof(buf) - 1] = '\0';

    TR("%s/memcheck: %s\n", prefix, buf);
}

/* Dumps content of memory allocation descriptor to a string.
 * Param:
 *  str - String to dump descriptor to.
 *  str_buf_size - Size of string's buffer.
 *  desc - Descriptor to dump.
 */
static void
dump_malloc_descriptor(char* str, size_t str_buf_size, const MallocDesc* desc)
{
    if (str_buf_size) {
        snprintf(str, str_buf_size,
            "MDesc: %p: %X <-> %X [%u + %u + %u] by pid=%03u in libc_pid=%03u",
            mallocdesc_user_ptr(desc), (uint32_t)desc->ptr,
            (uint32_t)mallocdesc_alloc_end(desc), desc->prefix_size,
            desc->requested_bytes, desc->suffix_size, desc->allocator_pid,
            desc->libc_pid);
        str[str_buf_size - 1] = '\0';
    }
}

#if TEST_ACCESS_VIOLATIONS
/* Causes an access violation on allocation descriptor, and verifies that
 * violation has been detected by memory checker in the emulator.
 */
static void
test_access_violation(const MallocDesc* desc)
{
    MallocDesc desc_chk;
    char ch;
    volatile char* prefix = (volatile char*)desc->ptr;
    volatile char* suffix = (volatile char*)mallocdesc_user_ptr(desc) +
                                            desc->requested_bytes;
    /* We're causing AV by reading from the prefix and suffix areas of the
     * allocated block. This should produce two access violations, so when we
     * get allocation descriptor from QEMU, av_counter should be bigger than
     * av_counter of the original descriptor by 2. */
    ch = *prefix;
    ch = *suffix;
    if (!query_qemu_malloc_info(mallocdesc_user_ptr(desc), &desc_chk, 2) &&
        desc_chk.av_count != (desc->av_count + 2)) {
        log_mdesc(error, &desc_chk,
                  "<libc_pid=%03u, pid=%03u>: malloc: Access violation test failed:\n"
                  "Expected violations count %u is not equal to the actually reported %u",
                  malloc_pid, getpid(), desc->av_count + 2,
                  desc_chk.av_count);
    }
}
#endif  // TEST_ACCESS_VIOLATIONS

// =============================================================================
// API routines
// =============================================================================

void* qemu_instrumented_malloc(size_t bytes);
void  qemu_instrumented_free(void* mem);
void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size);
void* qemu_instrumented_realloc(void* mem, size_t bytes);
void* qemu_instrumented_memalign(size_t alignment, size_t bytes);

/* Initializes malloc debugging instrumentation for the emulator.
 * This routine is called from malloc_init_impl routine implemented in
 * bionic/libc/bionic/malloc_debug_common.c when malloc debugging gets
 * initialized for a process. The way malloc debugging implementation is
 * done, it is guaranteed that this routine will be called just once per
 * process.
 * Return:
 *  0 on success, or -1 on failure.
*/
int
malloc_debug_initialize(void)
{
    /* We will be using emulator's magic page to report memory allocation
     * activities. In essence, what magic page does, it translates writes to
     * the memory mapped spaces into writes to an I/O port that emulator
     * "listens to" on the other end. Note that until we open and map that
     * device, logging to emulator's stdout will not be available. */
    int fd = open("/dev/qemu_trace", O_RDWR);
    if (fd < 0) {
        error_log("Unable to open /dev/qemu_trace");
        return -1;
    } else {
        qtrace = mmap(0, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        close(fd);

        if (qtrace == MAP_FAILED) {
            qtrace = NULL;
            error_log("Unable to mmap /dev/qemu_trace");
            return -1;
        }
    }

    /* Cache pid of the process this library has been initialized for. */
    malloc_pid = getpid();

    return 0;
}

/* Completes malloc debugging instrumentation for the emulator.
 * Note that this routine is called after successful return from
 * malloc_debug_initialize, which means that connection to the emulator via
 * "magic page" has been established.
 * Param:
 *  alignment - Alignment requirement set for memiry allocations.
 *  memcheck_param - Emulator's -memcheck option parameters. This string
 *      contains abbreviation for guest events that are enabled for tracing.
 * Return:
 *  0 on success, or -1 on failure.
*/
int
memcheck_initialize(int alignment, const char* memcheck_param)
{
    malloc_alignment = alignment;

    /* Parse -memcheck parameter for the guest tracing flags. */
    while (*memcheck_param != '\0') {
        switch (*memcheck_param) {
            case 'a':
                // Enable all messages from the guest.
                tracing_flags |= ALL_TRACING_ENABLED;
                break;
            case 'd':
                // Enable debug messages from the guest.
                tracing_flags |= DEBUG_TRACING_ENABLED;
                break;
            case 'e':
                // Enable error messages from the guest.
                tracing_flags |= ERROR_TRACING_ENABLED;
                break;
            case 'i':
                // Enable info messages from the guest.
                tracing_flags |= INFO_TRACING_ENABLED;
                break;
            default:
                break;
        }
        if (tracing_flags == ALL_TRACING_ENABLED) {
            break;
        }
        memcheck_param++;
    }

    notify_qemu_libc_initialized(malloc_pid);

    debug_log("Instrumented for pid=%03u: malloc=%p, free=%p, calloc=%p, realloc=%p, memalign=%p",
              malloc_pid, qemu_instrumented_malloc, qemu_instrumented_free,
              qemu_instrumented_calloc, qemu_instrumented_realloc,
              qemu_instrumented_memalign);

    return 0;
}

/* This routine serves as entry point for 'malloc'.
 * Primary responsibility of this routine is to allocate requested number of
 * bytes (plus prefix, and suffix guards), and report allocation to the
 * emulator.
 */
void*
qemu_instrumented_malloc(size_t bytes)
{
    MallocDesc desc;

    /* Initialize block descriptor and allocate memory. Note that dlmalloc
     * returns a valid pointer on zero allocation. Lets mimic this behavior. */
    desc.prefix_size = DEFAULT_PREFIX_SIZE;
    desc.requested_bytes = bytes;
    desc.suffix_size = DEFAULT_SUFFIX_SIZE;
    desc.ptr = dlmalloc(mallocdesc_alloc_size(&desc));
    if (desc.ptr == NULL) {
        error_log("<libc_pid=%03u, pid=%03u> malloc(%u): dlmalloc(%u) failed.",
                  malloc_pid, getpid(), bytes, mallocdesc_alloc_size(&desc));
        return NULL;
    }

    // Fire up event in the emulator.
    if (notify_qemu_malloc(&desc)) {
        log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: malloc: notify_malloc failed for ",
                  malloc_pid, getpid());
        dlfree(desc.ptr);
        return NULL;
    } else {
#if TEST_ACCESS_VIOLATIONS
        test_access_violation(&desc);
#endif  // TEST_ACCESS_VIOLATIONS
        log_mdesc(info, &desc, "+++ <libc_pid=%03u, pid=%03u> malloc(%u) -> ",
                  malloc_pid, getpid(), bytes);
        return mallocdesc_user_ptr(&desc);
    }
}

/* This routine serves as entry point for 'malloc'.
 * Primary responsibility of this routine is to free requested memory, and
 * report free block to the emulator.
 */
void
qemu_instrumented_free(void* mem)
{
    MallocDesc desc;

    if (mem == NULL) {
        // Just let go NULL free
        dlfree(mem);
        return;
    }

    // Query emulator for the freeing block information.
    if (query_qemu_malloc_info(mem, &desc, 1)) {
        error_log("<libc_pid=%03u, pid=%03u>: free(%p) query_info failed.",
                  malloc_pid, getpid(), mem);
        return;
    }

#if TEST_ACCESS_VIOLATIONS
    test_access_violation(&desc);
#endif  // TEST_ACCESS_VIOLATIONS

    /* Make sure that pointer that's being freed matches what we expect
     * for this memory block. Note that this violation should be already
     * caught in the emulator. */
    if (mem != mallocdesc_user_ptr(&desc)) {
        log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: free(%p) is invalid for ",
                  malloc_pid, getpid(), mem);
        return;
    }

    // Fire up event in the emulator and free block that was actually allocated.
    if (notify_qemu_free(mem)) {
        log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: free(%p) notify_free failed for ",
                  malloc_pid, getpid(), mem);
    } else {
        log_mdesc(info, &desc, "--- <libc_pid=%03u, pid=%03u> free(%p) -> ",
                  malloc_pid, getpid(), mem);
        dlfree(desc.ptr);
    }
}

/* This routine serves as entry point for 'calloc'.
 * This routine behaves similarly to qemu_instrumented_malloc.
 */
void*
qemu_instrumented_calloc(size_t n_elements, size_t elem_size)
{
    MallocDesc desc;
    void* ret;
    size_t total_size;
    size_t total_elements;

    if (n_elements == 0 || elem_size == 0) {
        // Just let go zero bytes allocation.
        info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc",
                 malloc_pid, getpid());
        return qemu_instrumented_malloc(0);
    }

    /* Fail on overflow - just to be safe even though this code runs only
     * within the debugging C library, not the production one */
    if (n_elements && MAX_SIZE_T / n_elements < elem_size) {
        return NULL;
    }

    /* Calculating prefix size. The trick here is to make sure that
     * first element (returned to the caller) is properly aligned. */
    if (DEFAULT_PREFIX_SIZE >= elem_size) {
        /* If default alignment is bigger than element size, we will
         * set our prefix size to the default alignment size. */
        desc.prefix_size = DEFAULT_PREFIX_SIZE;
        /* For the suffix we will use whatever bytes remain from the prefix
         * allocation size, aligned to the size of an element, plus the usual
         * default suffix size. */
        desc.suffix_size = (DEFAULT_PREFIX_SIZE % elem_size) +
                           DEFAULT_SUFFIX_SIZE;
    } else {
        /* Make sure that prefix, and suffix sizes is at least elem_size,
         * and first element returned to the caller is properly aligned. */
        desc.prefix_size = elem_size + DEFAULT_PREFIX_SIZE - 1;
        desc.prefix_size &= ~(malloc_alignment - 1);
        desc.suffix_size = DEFAULT_SUFFIX_SIZE;
    }
    desc.requested_bytes = n_elements * elem_size;
    total_size = desc.requested_bytes + desc.prefix_size + desc.suffix_size;
    total_elements = total_size / elem_size;
    total_size %= elem_size;
    if (total_size != 0) {
        // Add extra to the suffix area.
        total_elements++;
        desc.suffix_size += (elem_size - total_size);
    }
    desc.ptr = dlcalloc(total_elements, elem_size);
    if (desc.ptr == NULL) {
        error_log("<libc_pid=%03u, pid=%03u> calloc: dlcalloc(%u(%u), %u) (prx=%u, sfx=%u) failed.",
                   malloc_pid, getpid(), n_elements, total_elements, elem_size,
                   desc.prefix_size, desc.suffix_size);
        return NULL;
    }

    if (notify_qemu_malloc(&desc)) {
        log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: calloc(%u(%u), %u): notify_malloc failed for ",
                  malloc_pid, getpid(), n_elements, total_elements, elem_size);
        dlfree(desc.ptr);
        return NULL;
    } else {
#if TEST_ACCESS_VIOLATIONS
        test_access_violation(&desc);
#endif  // TEST_ACCESS_VIOLATIONS
        log_mdesc(info, &desc, "### <libc_pid=%03u, pid=%03u> calloc(%u(%u), %u) -> ",
                  malloc_pid, getpid(), n_elements, total_elements, elem_size);
        return mallocdesc_user_ptr(&desc);
    }
}

/* This routine serves as entry point for 'realloc'.
 * This routine behaves similarly to qemu_instrumented_free +
 * qemu_instrumented_malloc. Note that this modifies behavior of "shrinking" an
 * allocation, but overall it doesn't seem to matter, as caller of realloc
 * should not expect that pointer returned after shrinking will remain the same.
 */
void*
qemu_instrumented_realloc(void* mem, size_t bytes)
{
    MallocDesc new_desc;
    MallocDesc cur_desc;
    size_t to_copy;
    void* ret;

    if (mem == NULL) {
        // Nothing to realloc. just do regular malloc.
        info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to malloc",
                 malloc_pid, getpid(), mem, bytes);
        return qemu_instrumented_malloc(bytes);
    }

    if (bytes == 0) {
        // This is a "free" condition.
        info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to free and malloc",
                 malloc_pid, getpid(), mem, bytes);
        qemu_instrumented_free(mem);

        // This is what dlrealloc does for a "free" realloc.
        return NULL;
    }

    // Query emulator for the reallocating block information.
    if (query_qemu_malloc_info(mem, &cur_desc, 2)) {
        // Note that this violation should be already caught in the emulator.
        error_log("<libc_pid=%03u, pid=%03u>: realloc(%p, %u) query_info failed.",
                  malloc_pid, getpid(), mem, bytes);
        return NULL;
    }

#if TEST_ACCESS_VIOLATIONS
    test_access_violation(&cur_desc);
#endif  // TEST_ACCESS_VIOLATIONS

    /* Make sure that reallocating pointer value is what we would expect
     * for this memory block. Note that this violation should be already caught
     * in the emulator.*/
    if (mem != mallocdesc_user_ptr(&cur_desc)) {
        log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %u) is invalid for ",
                  malloc_pid, getpid(), mem, bytes);
        return NULL;
    }

    /* TODO: We're a bit inefficient here, always allocating new block from
     * the heap. If this realloc shrinks current buffer, we can just do the
     * shrinking "in place", adjusting suffix_size in the allocation descriptor
     * for this block that is stored in the emulator. */

    // Initialize descriptor for the new block.
    new_desc.prefix_size = DEFAULT_PREFIX_SIZE;
    new_desc.requested_bytes = bytes;
    new_desc.suffix_size = DEFAULT_SUFFIX_SIZE;
    new_desc.ptr = dlmalloc(mallocdesc_alloc_size(&new_desc));
    if (new_desc.ptr == NULL) {
        log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %u): dlmalloc(%u) failed on ",
                  malloc_pid, getpid(), mem, bytes,
                  mallocdesc_alloc_size(&new_desc));
        return NULL;
    }
    ret = mallocdesc_user_ptr(&new_desc);

    // Copy user data from old block to the new one.
    to_copy = bytes < cur_desc.requested_bytes ? bytes :
                                                 cur_desc.requested_bytes;
    if (to_copy != 0) {
        memcpy(ret, mallocdesc_user_ptr(&cur_desc), to_copy);
    }

    // Register new block with emulator.
    if(notify_qemu_malloc(&new_desc)) {
        log_mdesc(error, &new_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %u) notify_malloc failed -> ",
                  malloc_pid, getpid(), mem, bytes);
        log_mdesc(error, &cur_desc, "                                                                <- ");
        dlfree(new_desc.ptr);
        return NULL;
    }

#if TEST_ACCESS_VIOLATIONS
    test_access_violation(&new_desc);
#endif  // TEST_ACCESS_VIOLATIONS

    // Free old block.
    if (notify_qemu_free(mem)) {
        log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %u): notify_free failed for ",
                  malloc_pid, getpid(), mem, bytes);
        /* Since we registered new decriptor with the emulator, we need
         * to unregister it before freeing newly allocated block. */
        notify_qemu_free(mallocdesc_user_ptr(&new_desc));
        dlfree(new_desc.ptr);
        return NULL;
    }
    dlfree(cur_desc.ptr);

    log_mdesc(info, &new_desc, "=== <libc_pid=%03u, pid=%03u>: realloc(%p, %u) -> ",
              malloc_pid, getpid(), mem, bytes);
    log_mdesc(info, &cur_desc, "                                               <- ");

    return ret;
}

/* This routine serves as entry point for 'memalign'.
 * This routine behaves similarly to qemu_instrumented_malloc.
 */
void*
qemu_instrumented_memalign(size_t alignment, size_t bytes)
{
    MallocDesc desc;

    if (bytes == 0) {
        // Just let go zero bytes allocation.
        info_log("::: <libc_pid=%03u, pid=%03u>: memalign(%X, %u) redir to malloc",
                 malloc_pid, getpid(), alignment, bytes);
        return qemu_instrumented_malloc(0);
    }

    /* Prefix size for aligned allocation must be equal to the alignment used
     * for allocation in order to ensure proper alignment of the returned
     * pointer, in case that alignment requirement is greater than prefix
     * size. */
    desc.prefix_size = alignment > DEFAULT_PREFIX_SIZE ? alignment :
                                                         DEFAULT_PREFIX_SIZE;
    desc.requested_bytes = bytes;
    desc.suffix_size = DEFAULT_SUFFIX_SIZE;
    desc.ptr = dlmemalign(desc.prefix_size, mallocdesc_alloc_size(&desc));
    if (desc.ptr == NULL) {
        error_log("<libc_pid=%03u, pid=%03u> memalign(%X, %u): dlmalloc(%u) failed.",
                  malloc_pid, getpid(), alignment, bytes,
                  mallocdesc_alloc_size(&desc));
        return NULL;
    }
    if (notify_qemu_malloc(&desc)) {
        log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: memalign(%X, %u): notify_malloc failed for ",
                  malloc_pid, getpid(), alignment, bytes);
        dlfree(desc.ptr);
        return NULL;
    }

#if TEST_ACCESS_VIOLATIONS
    test_access_violation(&desc);
#endif  // TEST_ACCESS_VIOLATIONS

    log_mdesc(info, &desc, "@@@ <libc_pid=%03u, pid=%03u> memalign(%X, %u) -> ",
              malloc_pid, getpid(), alignment, bytes);
    return mallocdesc_user_ptr(&desc);
}