/* 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
#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)(ptrdiff_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)(ptrdiff_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)(ptrdiff_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