diff options
author | Vladimir Chtchetkine <vchtchetkine@google.com> | 2010-02-16 10:38:35 -0800 |
---|---|---|
committer | Vladimir Chtchetkine <vchtchetkine@google.com> | 2010-02-18 15:22:07 -0800 |
commit | 5389aa19033153c09556d1362a8b8a56abccb8f5 (patch) | |
tree | 5d731effe5bd5d2f162f06aadec7212045eaef3d | |
parent | 76dbca0489ab98a46f2954bc7b77c3df6f9d8264 (diff) | |
download | external_qemu-5389aa19033153c09556d1362a8b8a56abccb8f5.zip external_qemu-5389aa19033153c09556d1362a8b8a56abccb8f5.tar.gz external_qemu-5389aa19033153c09556d1362a8b8a56abccb8f5.tar.bz2 |
Merge memory checking from sandbox
Change-id: Ibce845d0
57 files changed, 12458 insertions, 137 deletions
@@ -1,2 +1,5 @@ images/android_icon.o objs/* +*.*~ +distrib + diff --git a/Makefile.android b/Makefile.android index 02518e3..35e2fcf 100644 --- a/Makefile.android +++ b/Makefile.android @@ -16,7 +16,7 @@ ifeq ($(BUILD_DEBUG_EMULATOR),true) MY_OPTIM := -O0 -g endif -MY_CFLAGS := $(CONFIG_INCLUDES) $(MY_OPTIM) +MY_CFLAGS := $(CONFIG_INCLUDES) $(MY_OPTIM) -DCONFIG_MEMCHECK # Overwrite configuration for debug builds. # @@ -193,6 +193,71 @@ LOCAL_SRC_FILES += $(HW_SOURCES:%=hw/%) include $(BUILD_HOST_STATIC_LIBRARY) ############################################################################## +# build the ELF/DWARF stuff +# This library is used by emulator's memory checker to extract debug information +# from the symbol files when reporting memory allocation violations. In +# particular, this library is used to extract routine name and source file +# location for the code address where violation has been detected. +# +include $(CLEAR_VARS) + +LOCAL_NO_DEFAULT_COMPILER_FLAGS := true +LOCAL_CC := $(MY_CC) +LOCAL_MODULE := emulator-elff +LOCAL_CPP_EXTENSION := .cc + +ELFF_CFLAGS := -I$(LOCAL_PATH)/elff + +LOCAL_CFLAGS := $(MY_CFLAGS) $(LOCAL_CFLAGS) +LOCAL_CFLAGS += -I$(LOCAL_PATH)/target-arm -I$(LOCAL_PATH)/fpu $(ELFF_CFLAGS) +LOCAL_CFLAGS += $(ZLIB_CFLAGS) -I$(LOCAL_PATH)/$(ZLIB_DIR) + +ELFF_SOURCES := \ + dwarf_cu.cc \ + dwarf_die.cc \ + dwarf_utils.cc \ + elf_alloc.cc \ + elf_file.cc \ + elf_mapped_section.cc \ + elff_api.cc \ + +LOCAL_SRC_FILES += $(ELFF_SOURCES:%=elff/%) +ELFF_LDLIBS := -lstdc++ + +include $(BUILD_HOST_STATIC_LIBRARY) + +############################################################################## +# build the memory access checking support +# Memory access checker uses information collected by instrumented code in +# libc.so in order to keep track of memory blocks allocated from heap. Memory +# checker then uses this information to make sure that every access to allocated +# memory is within allocated block. This information also allows detecting +# memory leaks and attempts to free/realloc invalid pointers. +# +include $(CLEAR_VARS) + +LOCAL_NO_DEFAULT_COMPILER_FLAGS := true +LOCAL_CC := $(MY_CC) +LOCAL_MODULE := emulator-memcheck + +MCHK_CFLAGS := -I$(LOCAL_PATH)/memcheck -I$(LOCAL_PATH)/elff + +LOCAL_CFLAGS := $(MY_CFLAGS) $(LOCAL_CFLAGS) +LOCAL_CFLAGS += -I$(LOCAL_PATH)/target-arm -I$(LOCAL_PATH)/fpu $(MCHK_CFLAGS) +LOCAL_CFLAGS += $(ZLIB_CFLAGS) -I$(LOCAL_PATH)/$(ZLIB_DIR) + +MCHK_SOURCES := \ + memcheck.c \ + memcheck_proc_management.c \ + memcheck_malloc_map.c \ + memcheck_mmrange_map.c \ + memcheck_util.c \ + +LOCAL_SRC_FILES += $(MCHK_SOURCES:%=memcheck/%) + +include $(BUILD_HOST_STATIC_LIBRARY) + +############################################################################## # build the ARM-specific emulation engine sources # include $(CLEAR_VARS) @@ -231,6 +296,7 @@ LOCAL_SRC_FILES += exec.c cpu-exec.c \ trace.c \ varint.c \ dcache.c \ + softmmu_outside_jit.c \ LOCAL_SRC_FILES += fpu/softfloat.c @@ -360,7 +426,8 @@ include $(CLEAR_VARS) LOCAL_NO_DEFAULT_COMPILER_FLAGS := true LOCAL_CC := $(MY_CC) LOCAL_MODULE := emulator -LOCAL_STATIC_LIBRARIES := emulator-hw emulator-arm emulator-tcg +LOCAL_STATIC_LIBRARIES := emulator-memcheck emulator-hw emulator-arm emulator-tcg +LOCAL_STATIC_LIBRARIES += emulator-elff LOCAL_LDLIBS := $(MY_LDLIBS) # don't remove the -fno-strict-aliasing, or you'll break things @@ -593,6 +660,10 @@ LOCAL_LDLIBS += $(SDL_LDLIBS) LOCAL_STATIC_LIBRARIES += libSDL libSDLmain LOCAL_STATIC_LIBRARIES += libSDL libSDLmain +# add ELFF-specific flags +# +LOCAL_LDLIBS += $(ELFF_LDLIBS) + # on Windows, link the icon file as well into the executable # unfortunately, our build system doesn't help us much, so we need # to use some weird pathnames to make this work... @@ -689,6 +760,7 @@ endif LOCAL_CFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE LOCAL_LDLIBS += -lm -lpthread + ifeq ($(HOST_OS),windows) LOCAL_LDLIBS += -lwinmm -lws2_32 -liphlpapi endif diff --git a/android/cmdline-options.h b/android/cmdline-options.h index fabac5c..20d308f 100644 --- a/android/cmdline-options.h +++ b/android/cmdline-options.h @@ -140,6 +140,9 @@ OPT_LIST( prop, "<name>=<value>", "set system property on boot") OPT_PARAM( nand_limits, "<nlimits>", "enforce NAND/Flash read/write thresholds" ) #endif +#ifdef CONFIG_MEMCHECK +OPT_PARAM( memcheck, "<flags>", "enable memory access checking" ) +#endif // CONFIG_MEMCHECK #undef CFG_FLAG #undef CFG_PARAM diff --git a/android/help.c b/android/help.c index cbca332..fe724a6 100644 --- a/android/help.c +++ b/android/help.c @@ -78,7 +78,7 @@ help_sdk_images( stralloc_t* out ) " Before using the SDK, you need to create an Android Virtual Device (AVD)\n" " (see -help-virtual-device for details). Each AVD is created in reference\n" " to a given SDK platform *or* add-on, and will search the corresponding\n" - " directories for system image files, in the following order:\n\n" + " directories for system image files, in the following order:\n\n" " - in the AVD's content directory.\n" " - in the AVD's SDK add-on directory, if any.\n" @@ -885,6 +885,45 @@ help_trace(stralloc_t* out) ); } +#ifdef CONFIG_MEMCHECK +static void +help_memcheck(stralloc_t* out) +{ + PRINTF( + " use '-memcheck <flags>' to start the emulator with memory access checking\n" + " support.\n\n" + + " <flags> enables, or disables memory access checking, and also controls\n" + " what events are going to be logged by the memory access checker.\n" + " <flags> can be one of the following:\n" + " 1 - Enables memory access checking with default logging (\"LIRW\"), or\n" + " 0 - Disables memory access checking, or\n" + " A combination (in no particular order) of the following:\n" + " L - Logs memory leaks on process exit.\n" + " I - Logs attempts to use invalid pointers in free, or realloc routines.\n" + " R - Logs memory access violation on read operations.\n" + " W - Logs memory access violation on write operations.\n" + " N - Logs new process ID allocation.\n" + " F - Logs guest's process forking.\n" + " S - Logs guest's process starting.\n" + " E - Logs guest's process exiting.\n" + " C - Logs guest's thread creation (clone).\n" + " B - Logs libc.so initialization in the guest system.\n" + " M - Logs module mapping and unmapping in the guest system.\n" + " A - Logs all emulator events. Equala to \"LIRWFSECANBM\" combination.\n" + " e - Logs error messages, received from the guest system.\n" + " d - Logs debug messages, received from the guest system.\n" + " i - Logs information messages, received from the guest system.\n" + " a - Logs all messages, received from the guest system.\n" + " This is equal to \"edi\" combination.\n\n" + + " note that execution might be significantly slower when enabling memory access\n" + " checking, this is a necessary requirement of the operations being performed\n" + " to analyze memory allocations and memory access.\n\n" + ); +} +#endif // CONFIG_MEMCHECK + static void help_show_kernel(stralloc_t* out) { @@ -1416,7 +1455,7 @@ android_help_for_topic( const char* topic, stralloc_t* out ) } -extern void +extern void android_help_list_options( stralloc_t* out ) { const OptionHelp* oo; diff --git a/android/main.c b/android/main.c index de048b9..309adf6 100644 --- a/android/main.c +++ b/android/main.c @@ -63,6 +63,9 @@ #include "android/cmdline-option.h" #include "android/help.h" #include "hw/goldfish_nand.h" +#ifdef CONFIG_MEMCHECK +#include "memcheck/memcheck.h" +#endif // CONFIG_MEMCHECK #include "android/globals.h" #include "tcpdump.h" @@ -1054,7 +1057,7 @@ void init_skinned_ui(const char *path, const char *name, AndroidOptions* opts) if(x && isdigit(x[1])) { int width = atoi(name); int height = atoi(x + 1); - sprintf(tmp,"display {\n width %d\n height %d\n}\n", + sprintf(tmp,"display {\n width %d\n height %d\n}\n", width, height); aconfig_load(root, strdup(tmp)); path = ":"; @@ -1075,7 +1078,7 @@ void init_skinned_ui(const char *path, const char *name, AndroidOptions* opts) path = tmp; goto found_a_skin; } else { - dwarning("could not load skin file '%s', using built-in one\n", + dwarning("could not load skin file '%s', using built-in one\n", tmp); } } @@ -1215,7 +1218,7 @@ android_parse_network_speed(const char* speed) } if (android_modem) - amodem_set_data_network_type( android_modem, + amodem_set_data_network_type( android_modem, android_parse_network_type(speed) ); return 0; } @@ -1739,8 +1742,8 @@ _getSdkSystemImage( const char* path, const char* optionName, const char* fil } static void -_forceAvdImagePath( AvdImageType imageType, - const char* path, +_forceAvdImagePath( AvdImageType imageType, + const char* path, const char* description, int required ) { @@ -1993,7 +1996,7 @@ int main(int argc, char **argv) * Android build system, we'll need to perform some auto-detection * magic :-) */ - if (opts->avd == NULL && !android_build_out) + if (opts->avd == NULL && !android_build_out) { char dataDirIsSystem = 0; @@ -2297,6 +2300,12 @@ int main(int argc, char **argv) opts->trace = tracePath; } +#ifdef CONFIG_MEMCHECK + if (opts->memcheck) { + memcheck_init(opts->memcheck); + } +#endif // CONFIG_MEMCHECK + if (opts->tcpdump) { if (qemu_tcpdump_start(opts->tcpdump) < 0) { dwarning( "could not start packet capture: %s", strerror(errno)); @@ -2705,6 +2714,14 @@ int main(int argc, char **argv) p = bufprint(p, end, " android.tracing=1"); } +#ifdef CONFIG_MEMCHECK + if (opts->memcheck) { + /* This will set ro.kernel.memcheck system property + * to memcheck's tracing flags. */ + p = bufprint(p, end, " memcheck=%s", opts->memcheck); + } +#endif // CONFIG_MEMCHECK + if (!opts->no_jni) { p = bufprint(p, end, " android.checkjni=1"); } diff --git a/android/utils/debug.h b/android/utils/debug.h index d6bd3f9..06b9baf 100644 --- a/android/utils/debug.h +++ b/android/utils/debug.h @@ -34,6 +34,7 @@ _VERBOSE_TAG(hw_control, "emulated power/flashlight/led/vibrator") \ _VERBOSE_TAG(avd_config, "android virtual device configuration") \ _VERBOSE_TAG(sensors, "emulated sensors") \ + _VERBOSE_TAG(memcheck, "memory checker") \ #define _VERBOSE_TAG(x,y) VERBOSE_##x, typedef enum { diff --git a/android/utils/path.h b/android/utils/path.h index e822834..e15e6ed 100644 --- a/android/utils/path.h +++ b/android/utils/path.h @@ -28,8 +28,10 @@ /* define PATH_SEP as a string containing the directory separateor */ #ifdef _WIN32 # define PATH_SEP "\\" +# define PATH_SEP_C '\\' #else # define PATH_SEP "/" +# define PATH_SEP_C '/' #endif /* get MAX_PATH, note that PATH_MAX is set to 260 on Windows for @@ -65,7 +67,7 @@ extern ABool path_can_write( const char* path ); /* try to make a directory */ extern APosixStatus path_mkdir( const char* path, int mode ); -/* ensure that a given directory exists, create it if not, +/* ensure that a given directory exists, create it if not, 0 on success, -1 on error */ extern APosixStatus path_mkdir_if_needed( const char* path, int mode ); diff --git a/docs/ANDROID-ELFF.TXT b/docs/ANDROID-ELFF.TXT new file mode 100644 index 0000000..afb3015 --- /dev/null +++ b/docs/ANDROID-ELFF.TXT @@ -0,0 +1,23 @@ +ANDROID ELFF LIBRARY + +The docs/ANDROID-ELFF.TXT document contains notes about the ELFF library used +by the emulator to extract debugging information from symbol files of the +emulated system. + +The ELFF library has been introduced to provide the emulator's memory checker +subsystem with information about routine name, source file name and location +when a memory access violation detected at runtime. This requires parsing symbol +files corresponding to the libraries of the emulated system, in order to extract +relevant information from them. + +Due to lack of windows support on available open source projects, this is a +full new implementation of an ELF/DWARF parser. + +The library exposes a simple API that at this point allows: +- Extract information about routine, containing given PC address in the emulated + system. Routine information, collected by the library includes: + * Routine name + * Source file name and line corresponded to the given emulated PC. + * If routine happens to be inlined, library traces inclusion up to the first + routine that is not inlined, providing names and source file location + information for all inlined routines in that chain. diff --git a/docs/ANDROID-MEMCHECK.TXT b/docs/ANDROID-MEMCHECK.TXT new file mode 100644 index 0000000..0c5e716 --- /dev/null +++ b/docs/ANDROID-MEMCHECK.TXT @@ -0,0 +1,57 @@ +ANDROID MEMORY CHECKER COMPONENT + +The docs/ANDROID-MEMCHECK.TXT document contains description of a memory checker +implemented in the emulator + +The memory checker is intended to catch simple memory access violations in the +emulated system, including: +- Memory leaks +- Attempts to free / reallocate invalid pointers (including pointers that have + already been freed, or reallocated). +- Attempts to read from / write to areas outside of allocated blocks. + +To provide this functionality, the memory checker works in conjunction with +an instrumented version of libc.so library used by the emulated system. Both, +emulator's memory checking, and libc's instrumentation are turned on by the +-memcheck command-line option. If this argument is omitted, libc.so will run in +the regular, not instrumented mode, and the emulator will not perform any +actions related to the memory checking. + +The way emulator and instrumented libc.so work together is as such: +libc.so hooks up every memory allocation call (malloc, free, calloc, realloc, +and memalign). For each such call, libc sends a notification message to the +emulator, providing an allocation descriptor that contains information about +allocation block and operation that is being performed on this block. Emulator +and libc use a "magic page" that is set up in such a way that every write to +that page on the emulated system produces some sort of event in the emulator, +allowing emulator to receive data that emulated system has written to the "magic +page". For more info on that, see hw/goldfish-trace.c + +In response to events, received from libc.so, emulator keep tracks of all blocks +that have been allocated from the heap on emulated system. Block descriptors are +kept in a per-process basis, so when a process exits, emulator can list all the +leaked blocks the process left behind. + +When a free, or realloc operation is performed on the emulated system, emulator +can verify that the pointer passed to free/realloc matches the address of a +block recorded in the current process' descriptors table. This way emulator can +detect and report attempts to free/reallocate invalid pointers. + +To detect read/write violations, emulator uses prefix and suffix guarding areas +that were added to the allocated blocks by the instrumented libc. To watch for +access violations, emulator instruments ld_/st_mmu routines to verify that +accessed memory doesn't belong to a guarding area of a block allocated in +context of the current process. + +There are some tricky things like: +- invalidating every page containing allocated blocks every time anything has + been read, or written to that page, so we can be sure that we don't miss AV + on condition that page has been cached and ld_/st_mmu is omitted when + accessing memory in that page. +- Keeping track of each thread calling stack, so when access violation is + reported, we can pinpoint violation to precise location in the source code. +- etc. + +All the code related to memory checking is guarded in emulator's code by +CONFIG_MEMCHECK macro, making it easy to spot changes related to it in the +sources. diff --git a/elff/dwarf.h b/elff/dwarf.h new file mode 100644 index 0000000..31ef714 --- /dev/null +++ b/elff/dwarf.h @@ -0,0 +1,1055 @@ +/* + Copyright (C) 2000,2001,2003,2004,2005,2006 Silicon Graphics, Inc. All Rights Reserved. + Portions Copyright 2002,2007 Sun Microsystems, Inc. All rights reserved. + Portions Copyright 2007-2009 David Anderson. All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2.1 of the GNU Lesser General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it would be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + Further, this software is distributed without any warranty that it is + free of the rightful claim of any third person regarding infringement + or the like. Any license provided herein, whether implied or + otherwise, applies only to this software file. Patent licenses, if + any, provided herein do not apply to combinations of this program with + other software, or any other product whatsoever. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301, + USA. + + Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane, + Mountain View, CA 94043, or: + + http://www.sgi.com + + For further information regarding this notice, see: + + http://oss.sgi.com/projects/GenInfo/NoticeExplan + +*/ + + +#ifndef __DWARF_H +#define __DWARF_H +#ifdef __cplusplus +extern "C" { +#endif + +/* + dwarf.h DWARF debugging information values + $Revision: 1.41 $ $Date: 2006/04/17 00:09:56 $ + + The comment "DWARF3" appears where there are + new entries from DWARF3 as of 2004, "DWARF3f" + where there are new entries as of the November 2005 + public review document and other comments apply + where extension entries appear. + + Extensions part of DWARF4 are marked DWARF4. + + A few extension names have omitted the 'vendor id' + (See chapter 7, "Vendor Extensibility"). Please + always use a 'vendor id' string in extension names. + + Vendors should use a vendor string in names and + whereever possible avoid duplicating values used by + other vendor extensions + +*/ + + +#define DW_TAG_array_type 0x01 +#define DW_TAG_class_type 0x02 +#define DW_TAG_entry_point 0x03 +#define DW_TAG_enumeration_type 0x04 +#define DW_TAG_formal_parameter 0x05 +#define DW_TAG_imported_declaration 0x08 +#define DW_TAG_label 0x0a +#define DW_TAG_lexical_block 0x0b +#define DW_TAG_member 0x0d +#define DW_TAG_pointer_type 0x0f +#define DW_TAG_reference_type 0x10 +#define DW_TAG_compile_unit 0x11 +#define DW_TAG_string_type 0x12 +#define DW_TAG_structure_type 0x13 +#define DW_TAG_subroutine_type 0x15 +#define DW_TAG_typedef 0x16 +#define DW_TAG_union_type 0x17 +#define DW_TAG_unspecified_parameters 0x18 +#define DW_TAG_variant 0x19 +#define DW_TAG_common_block 0x1a +#define DW_TAG_common_inclusion 0x1b +#define DW_TAG_inheritance 0x1c +#define DW_TAG_inlined_subroutine 0x1d +#define DW_TAG_module 0x1e +#define DW_TAG_ptr_to_member_type 0x1f +#define DW_TAG_set_type 0x20 +#define DW_TAG_subrange_type 0x21 +#define DW_TAG_with_stmt 0x22 +#define DW_TAG_access_declaration 0x23 +#define DW_TAG_base_type 0x24 +#define DW_TAG_catch_block 0x25 +#define DW_TAG_const_type 0x26 +#define DW_TAG_constant 0x27 +#define DW_TAG_enumerator 0x28 +#define DW_TAG_file_type 0x29 +#define DW_TAG_friend 0x2a +#define DW_TAG_namelist 0x2b + /* Early releases of this header had the following + misspelled with a trailing 's' */ +#define DW_TAG_namelist_item 0x2c /* DWARF3/2 spelling */ +#define DW_TAG_namelist_items 0x2c /* SGI misspelling/typo */ +#define DW_TAG_packed_type 0x2d +#define DW_TAG_subprogram 0x2e + /* The DWARF2 document had two spellings of the following + two TAGs, DWARF3 specifies the longer spelling. */ +#define DW_TAG_template_type_parameter 0x2f /* DWARF3/2 spelling*/ +#define DW_TAG_template_type_param 0x2f /* DWARF2 spelling*/ +#define DW_TAG_template_value_parameter 0x30 /* DWARF3/2 spelling*/ +#define DW_TAG_template_value_param 0x30 /* DWARF2 spelling*/ +#define DW_TAG_thrown_type 0x31 +#define DW_TAG_try_block 0x32 +#define DW_TAG_variant_part 0x33 +#define DW_TAG_variable 0x34 +#define DW_TAG_volatile_type 0x35 +#define DW_TAG_dwarf_procedure 0x36 /* DWARF3 */ +#define DW_TAG_restrict_type 0x37 /* DWARF3 */ +#define DW_TAG_interface_type 0x38 /* DWARF3 */ +#define DW_TAG_namespace 0x39 /* DWARF3 */ +#define DW_TAG_imported_module 0x3a /* DWARF3 */ +#define DW_TAG_unspecified_type 0x3b /* DWARF3 */ +#define DW_TAG_partial_unit 0x3c /* DWARF3 */ +#define DW_TAG_imported_unit 0x3d /* DWARF3 */ + /* Do not use DW_TAG_mutable_type */ +#define DW_TAG_mutable_type 0x3e /* Withdrawn from DWARF3 by DWARF3f. */ +#define DW_TAG_condition 0x3f /* DWARF3f */ +#define DW_TAG_shared_type 0x40 /* DWARF3f */ +#define DW_TAG_type_unit 0x41 /* DWARF4 */ +#define DW_TAG_rvalue_reference_type 0x42 /* DWARF4 */ +#define DW_TAG_template_alias 0x43 /* DWARF4 */ +#define DW_TAG_lo_user 0x4080 + +#define DW_TAG_MIPS_loop 0x4081 + +/* HP extensions: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz */ +#define DW_TAG_HP_array_descriptor 0x4090 /* HP */ + +/* GNU extensions. The first 3 missing the GNU_. */ +#define DW_TAG_format_label 0x4101 /* GNU. Fortran. */ +#define DW_TAG_function_template 0x4102 /* GNU. For C++ */ +#define DW_TAG_class_template 0x4103 /* GNU. For C++ */ +#define DW_TAG_GNU_BINCL 0x4104 /* GNU */ +#define DW_TAG_GNU_EINCL 0x4105 /* GNU */ + +/* ALTIUM extensions */ + /* DSP-C/Starcore __circ qualifier */ +#define DW_TAG_ALTIUM_circ_type 0x5101 /* ALTIUM */ + /* Starcore __mwa_circ qualifier */ +#define DW_TAG_ALTIUM_mwa_circ_type 0x5102 /* ALTIUM */ + /* Starcore __rev_carry qualifier */ +#define DW_TAG_ALTIUM_rev_carry_type 0x5103 /* ALTIUM */ + /* M16 __rom qualifier */ +#define DW_TAG_ALTIUM_rom 0x5111 /* ALTIUM */ + +/* The following 3 are extensions to support UPC */ +#define DW_TAG_upc_shared_type 0x8765 /* UPC */ +#define DW_TAG_upc_strict_type 0x8766 /* UPC */ +#define DW_TAG_upc_relaxed_type 0x8767 /* UPC */ + +/* PGI (STMicroelectronics) extensions. */ +#define DW_TAG_PGI_kanji_type 0xa000 /* PGI */ +#define DW_TAG_PGI_interface_block 0xa020 /* PGI */ +/* The following are SUN extensions */ +#define DW_TAG_SUN_function_template 0x4201 /* SUN */ +#define DW_TAG_SUN_class_template 0x4202 /* SUN */ +#define DW_TAG_SUN_struct_template 0x4203 /* SUN */ +#define DW_TAG_SUN_union_template 0x4204 /* SUN */ +#define DW_TAG_SUN_indirect_inheritance 0x4205 /* SUN */ +#define DW_TAG_SUN_codeflags 0x4206 /* SUN */ +#define DW_TAG_SUN_memop_info 0x4207 /* SUN */ +#define DW_TAG_SUN_omp_child_func 0x4208 /* SUN */ +#define DW_TAG_SUN_rtti_descriptor 0x4209 /* SUN */ +#define DW_TAG_SUN_dtor_info 0x420a /* SUN */ +#define DW_TAG_SUN_dtor 0x420b /* SUN */ +#define DW_TAG_SUN_f90_interface 0x420c /* SUN */ +#define DW_TAG_SUN_fortran_vax_structure 0x420d /* SUN */ +#define DW_TAG_SUN_hi 0x42ff /* SUN */ + + +#define DW_TAG_hi_user 0xffff + +#define DW_children_no 0 +#define DW_children_yes 1 + + + +#define DW_FORM_addr 0x01 +#define DW_FORM_block2 0x03 +#define DW_FORM_block4 0x04 +#define DW_FORM_data2 0x05 +#define DW_FORM_data4 0x06 +#define DW_FORM_data8 0x07 +#define DW_FORM_string 0x08 +#define DW_FORM_block 0x09 +#define DW_FORM_block1 0x0a +#define DW_FORM_data1 0x0b +#define DW_FORM_flag 0x0c +#define DW_FORM_sdata 0x0d +#define DW_FORM_strp 0x0e +#define DW_FORM_udata 0x0f +#define DW_FORM_ref_addr 0x10 +#define DW_FORM_ref1 0x11 +#define DW_FORM_ref2 0x12 +#define DW_FORM_ref4 0x13 +#define DW_FORM_ref8 0x14 +#define DW_FORM_ref_udata 0x15 +#define DW_FORM_indirect 0x16 +#define DW_FORM_sec_offset 0x17 /* DWARF4 */ +#define DW_FORM_exprloc 0x18 /* DWARF4 */ +#define DW_FORM_flag_present 0x19 /* DWARF4 */ +#define DW_FORM_ref_sig8 0x20 /* DWARF4 */ + +#define DW_AT_sibling 0x01 +#define DW_AT_location 0x02 +#define DW_AT_name 0x03 +#define DW_AT_ordering 0x09 +#define DW_AT_subscr_data 0x0a +#define DW_AT_byte_size 0x0b +#define DW_AT_bit_offset 0x0c +#define DW_AT_bit_size 0x0d +#define DW_AT_element_list 0x0f +#define DW_AT_stmt_list 0x10 +#define DW_AT_low_pc 0x11 +#define DW_AT_high_pc 0x12 +#define DW_AT_language 0x13 +#define DW_AT_member 0x14 +#define DW_AT_discr 0x15 +#define DW_AT_discr_value 0x16 +#define DW_AT_visibility 0x17 +#define DW_AT_import 0x18 +#define DW_AT_string_length 0x19 +#define DW_AT_common_reference 0x1a +#define DW_AT_comp_dir 0x1b +#define DW_AT_const_value 0x1c +#define DW_AT_containing_type 0x1d +#define DW_AT_default_value 0x1e +#define DW_AT_inline 0x20 +#define DW_AT_is_optional 0x21 +#define DW_AT_lower_bound 0x22 +#define DW_AT_producer 0x25 +#define DW_AT_prototyped 0x27 +#define DW_AT_return_addr 0x2a +#define DW_AT_start_scope 0x2c +#define DW_AT_bit_stride 0x2e /* DWARF3 name */ +#define DW_AT_stride_size 0x2e /* DWARF2 name */ +#define DW_AT_upper_bound 0x2f +#define DW_AT_abstract_origin 0x31 +#define DW_AT_accessibility 0x32 +#define DW_AT_address_class 0x33 +#define DW_AT_artificial 0x34 +#define DW_AT_base_types 0x35 +#define DW_AT_calling_convention 0x36 +#define DW_AT_count 0x37 +#define DW_AT_data_member_location 0x38 +#define DW_AT_decl_column 0x39 +#define DW_AT_decl_file 0x3a +#define DW_AT_decl_line 0x3b +#define DW_AT_declaration 0x3c +#define DW_AT_discr_list 0x3d +#define DW_AT_encoding 0x3e +#define DW_AT_external 0x3f +#define DW_AT_frame_base 0x40 +#define DW_AT_friend 0x41 +#define DW_AT_identifier_case 0x42 +#define DW_AT_macro_info 0x43 +#define DW_AT_namelist_item 0x44 +#define DW_AT_priority 0x45 +#define DW_AT_segment 0x46 +#define DW_AT_specification 0x47 +#define DW_AT_static_link 0x48 +#define DW_AT_type 0x49 +#define DW_AT_use_location 0x4a +#define DW_AT_variable_parameter 0x4b +#define DW_AT_virtuality 0x4c +#define DW_AT_vtable_elem_location 0x4d +#define DW_AT_allocated 0x4e /* DWARF3 */ +#define DW_AT_associated 0x4f /* DWARF3 */ +#define DW_AT_data_location 0x50 /* DWARF3 */ +#define DW_AT_byte_stride 0x51 /* DWARF3f */ +#define DW_AT_stride 0x51 /* DWARF3 (do not use) */ +#define DW_AT_entry_pc 0x52 /* DWARF3 */ +#define DW_AT_use_UTF8 0x53 /* DWARF3 */ +#define DW_AT_extension 0x54 /* DWARF3 */ +#define DW_AT_ranges 0x55 /* DWARF3 */ +#define DW_AT_trampoline 0x56 /* DWARF3 */ +#define DW_AT_call_column 0x57 /* DWARF3 */ +#define DW_AT_call_file 0x58 /* DWARF3 */ +#define DW_AT_call_line 0x59 /* DWARF3 */ +#define DW_AT_description 0x5a /* DWARF3 */ +#define DW_AT_binary_scale 0x5b /* DWARF3f */ +#define DW_AT_decimal_scale 0x5c /* DWARF3f */ +#define DW_AT_small 0x5d /* DWARF3f */ +#define DW_AT_decimal_sign 0x5e /* DWARF3f */ +#define DW_AT_digit_count 0x5f /* DWARF3f */ +#define DW_AT_picture_string 0x60 /* DWARF3f */ +#define DW_AT_mutable 0x61 /* DWARF3f */ +#define DW_AT_threads_scaled 0x62 /* DWARF3f */ +#define DW_AT_explicit 0x63 /* DWARF3f */ +#define DW_AT_object_pointer 0x64 /* DWARF3f */ +#define DW_AT_endianity 0x65 /* DWARF3f */ +#define DW_AT_elemental 0x66 /* DWARF3f */ +#define DW_AT_pure 0x67 /* DWARF3f */ +#define DW_AT_recursive 0x68 /* DWARF3f */ +#define DW_AT_signature 0x69 /* DWARF4 */ +#define DW_AT_main_subprogram 0x6a /* DWARF4 */ +#define DW_AT_data_bit_offset 0x6b /* DWARF4 */ +#define DW_AT_const_expr 0x6c /* DWARF4 */ +#define DW_AT_enum_class 0x6d /* DWARF4 */ +#define DW_AT_linkage_name 0x6e /* DWARF4 */ + +/* In extensions, we attempt to include the vendor extension + in the name even when the vendor leaves it out. */ + +/* HP extensions. */ +#define DW_AT_HP_block_index 0x2000 /* HP */ + +/* Follows extension so dwarfdump prints the most-likely-useful name. */ +#define DW_AT_lo_user 0x2000 + +#define DW_AT_MIPS_fde 0x2001 /* MIPS/SGI */ +#define DW_AT_MIPS_loop_begin 0x2002 /* MIPS/SGI */ +#define DW_AT_MIPS_tail_loop_begin 0x2003 /* MIPS/SGI */ +#define DW_AT_MIPS_epilog_begin 0x2004 /* MIPS/SGI */ +#define DW_AT_MIPS_loop_unroll_factor 0x2005 /* MIPS/SGI */ +#define DW_AT_MIPS_software_pipeline_depth 0x2006 /* MIPS/SGI */ +#define DW_AT_MIPS_linkage_name 0x2007 /* MIPS/SGI, GNU, and others.*/ +#define DW_AT_MIPS_stride 0x2008 /* MIPS/SGI */ +#define DW_AT_MIPS_abstract_name 0x2009 /* MIPS/SGI */ +#define DW_AT_MIPS_clone_origin 0x200a /* MIPS/SGI */ +#define DW_AT_MIPS_has_inlines 0x200b /* MIPS/SGI */ +#define DW_AT_MIPS_stride_byte 0x200c /* MIPS/SGI */ +#define DW_AT_MIPS_stride_elem 0x200d /* MIPS/SGI */ +#define DW_AT_MIPS_ptr_dopetype 0x200e /* MIPS/SGI */ +#define DW_AT_MIPS_allocatable_dopetype 0x200f /* MIPS/SGI */ +#define DW_AT_MIPS_assumed_shape_dopetype 0x2010 /* MIPS/SGI */ +#define DW_AT_MIPS_assumed_size 0x2011 /* MIPS/SGI */ + +/* HP extensions. */ +#define DW_AT_HP_unmodifiable 0x2001 /* conflict: MIPS */ +#define DW_AT_HP_actuals_stmt_list 0x2010 /* conflict: MIPS */ +#define DW_AT_HP_proc_per_section 0x2011 /* conflict: MIPS */ +#define DW_AT_HP_raw_data_ptr 0x2012 /* HP */ +#define DW_AT_HP_pass_by_reference 0x2013 /* HP */ +#define DW_AT_HP_opt_level 0x2014 /* HP */ +#define DW_AT_HP_prof_version_id 0x2015 /* HP */ +#define DW_AT_HP_opt_flags 0x2016 /* HP */ +#define DW_AT_HP_cold_region_low_pc 0x2017 /* HP */ +#define DW_AT_HP_cold_region_high_pc 0x2018 /* HP */ +#define DW_AT_HP_all_variables_modifiable 0x2019 /* HP */ +#define DW_AT_HP_linkage_name 0x201a /* HP */ +#define DW_AT_HP_prof_flags 0x201b /* HP */ + +#define DW_AT_CPQ_discontig_ranges 0x2001 /* COMPAQ/HP */ +#define DW_AT_CPQ_semantic_events 0x2002 /* COMPAQ/HP */ +#define DW_AT_CPQ_split_lifetimes_var 0x2003 /* COMPAQ/HP */ +#define DW_AT_CPQ_split_lifetimes_rtn 0x2004 /* COMPAQ/HP */ +#define DW_AT_CPQ_prologue_length 0x2005 /* COMPAQ/HP */ + +#define DW_AT_INTEL_other_endian 0x2026 /* Intel, 1 if byte swapped. */ + +/* GNU extensions. */ +#define DW_AT_sf_names 0x2101 /* GNU */ +#define DW_AT_src_info 0x2102 /* GNU */ +#define DW_AT_mac_info 0x2103 /* GNU */ +#define DW_AT_src_coords 0x2104 /* GNU */ +#define DW_AT_body_begin 0x2105 /* GNU */ +#define DW_AT_body_end 0x2106 /* GNU */ +#define DW_AT_GNU_vector 0x2107 /* GNU */ + +/* ALTIUM extension: ALTIUM Compliant location lists (flag) */ +#define DW_AT_ALTIUM_loclist 0x2300 /* ALTIUM */ + +/* Sun extensions */ +#define DW_AT_SUN_template 0x2201 /* SUN */ +#define DW_AT_VMS_rtnbeg_pd_address 0x2201 /* VMS */ +#define DW_AT_SUN_alignment 0x2202 /* SUN */ +#define DW_AT_SUN_vtable 0x2203 /* SUN */ +#define DW_AT_SUN_count_guarantee 0x2204 /* SUN */ +#define DW_AT_SUN_command_line 0x2205 /* SUN */ +#define DW_AT_SUN_vbase 0x2206 /* SUN */ +#define DW_AT_SUN_compile_options 0x2207 /* SUN */ +#define DW_AT_SUN_language 0x2208 /* SUN */ +#define DW_AT_SUN_browser_file 0x2209 /* SUN */ +#define DW_AT_SUN_vtable_abi 0x2210 /* SUN */ +#define DW_AT_SUN_func_offsets 0x2211 /* SUN */ +#define DW_AT_SUN_cf_kind 0x2212 /* SUN */ +#define DW_AT_SUN_vtable_index 0x2213 /* SUN */ +#define DW_AT_SUN_omp_tpriv_addr 0x2214 /* SUN */ +#define DW_AT_SUN_omp_child_func 0x2215 /* SUN */ +#define DW_AT_SUN_func_offset 0x2216 /* SUN */ +#define DW_AT_SUN_memop_type_ref 0x2217 /* SUN */ +#define DW_AT_SUN_profile_id 0x2218 /* SUN */ +#define DW_AT_SUN_memop_signature 0x2219 /* SUN */ +#define DW_AT_SUN_obj_dir 0x2220 /* SUN */ +#define DW_AT_SUN_obj_file 0x2221 /* SUN */ +#define DW_AT_SUN_original_name 0x2222 /* SUN */ +#define DW_AT_SUN_hwcprof_signature 0x2223 /* SUN */ +#define DW_AT_SUN_amd64_parmdump 0x2224 /* SUN */ +#define DW_AT_SUN_part_link_name 0x2225 /* SUN */ +#define DW_AT_SUN_link_name 0x2226 /* SUN */ +#define DW_AT_SUN_pass_with_const 0x2227 /* SUN */ +#define DW_AT_SUN_return_with_const 0x2228 /* SUN */ +#define DW_AT_SUN_import_by_name 0x2229 /* SUN */ +#define DW_AT_SUN_f90_pointer 0x222a /* SUN */ +#define DW_AT_SUN_pass_by_ref 0x222b /* SUN */ +#define DW_AT_SUN_f90_allocatable 0x222c /* SUN */ +#define DW_AT_SUN_f90_assumed_shape_array 0x222d /* SUN */ +#define DW_AT_SUN_c_vla 0x222e /* SUN */ +#define DW_AT_SUN_return_value_ptr 0x2230 /* SUN */ +#define DW_AT_SUN_dtor_start 0x2231 /* SUN */ +#define DW_AT_SUN_dtor_length 0x2232 /* SUN */ +#define DW_AT_SUN_dtor_state_initial 0x2233 /* SUN */ +#define DW_AT_SUN_dtor_state_final 0x2234 /* SUN */ +#define DW_AT_SUN_dtor_state_deltas 0x2235 /* SUN */ +#define DW_AT_SUN_import_by_lname 0x2236 /* SUN */ +#define DW_AT_SUN_f90_use_only 0x2237 /* SUN */ +#define DW_AT_SUN_namelist_spec 0x2238 /* SUN */ +#define DW_AT_SUN_is_omp_child_func 0x2239 /* SUN */ +#define DW_AT_SUN_fortran_main_alias 0x223a /* SUN */ +#define DW_AT_SUN_fortran_based 0x223b /* SUN */ + +/* UPC extension */ +#define DW_AT_upc_threads_scaled 0x3210 /* UPC */ + +/* PGI (STMicroelectronics) extensions. */ +#define DW_AT_PGI_lbase 0x3a00 /* PGI. Block, constant, reference. This attribute is an ASTPLAB extension used to describe the array local base. */ +#define DW_AT_PGI_soffset 0x3a01 /* PGI. Block, constant, reference. ASTPLAB adds this attribute to describe the section offset, or the offset to the first element in the dimension. */ +#define DW_AT_PGI_lstride 0x3a02 /* PGI. Block, constant, reference. ASTPLAB adds this attribute to describe the linear stride or the distance between elements in the dimension. */ + +/* Apple Extensions for closures */ +#define DW_AT_APPLE_closure 0x3fe4 /* Apple */ +/* Apple Extensions for Objective-C runtime info */ +#define DW_AT_APPLE_major_runtime_vers 0x3fe5 /* Apple */ +#define DW_AT_APPLE_runtime_class 0x3fe6 /* Apple */ + + +#define DW_AT_hi_user 0x3fff + +#define DW_OP_addr 0x03 +#define DW_OP_deref 0x06 +#define DW_OP_const1u 0x08 +#define DW_OP_const1s 0x09 +#define DW_OP_const2u 0x0a +#define DW_OP_const2s 0x0b +#define DW_OP_const4u 0x0c +#define DW_OP_const4s 0x0d +#define DW_OP_const8u 0x0e +#define DW_OP_const8s 0x0f +#define DW_OP_constu 0x10 +#define DW_OP_consts 0x11 +#define DW_OP_dup 0x12 +#define DW_OP_drop 0x13 +#define DW_OP_over 0x14 +#define DW_OP_pick 0x15 +#define DW_OP_swap 0x16 +#define DW_OP_rot 0x17 +#define DW_OP_xderef 0x18 +#define DW_OP_abs 0x19 +#define DW_OP_and 0x1a +#define DW_OP_div 0x1b +#define DW_OP_minus 0x1c +#define DW_OP_mod 0x1d +#define DW_OP_mul 0x1e +#define DW_OP_neg 0x1f +#define DW_OP_not 0x20 +#define DW_OP_or 0x21 +#define DW_OP_plus 0x22 +#define DW_OP_plus_uconst 0x23 +#define DW_OP_shl 0x24 +#define DW_OP_shr 0x25 +#define DW_OP_shra 0x26 +#define DW_OP_xor 0x27 +#define DW_OP_bra 0x28 +#define DW_OP_eq 0x29 +#define DW_OP_ge 0x2a +#define DW_OP_gt 0x2b +#define DW_OP_le 0x2c +#define DW_OP_lt 0x2d +#define DW_OP_ne 0x2e +#define DW_OP_skip 0x2f +#define DW_OP_lit0 0x30 +#define DW_OP_lit1 0x31 +#define DW_OP_lit2 0x32 +#define DW_OP_lit3 0x33 +#define DW_OP_lit4 0x34 +#define DW_OP_lit5 0x35 +#define DW_OP_lit6 0x36 +#define DW_OP_lit7 0x37 +#define DW_OP_lit8 0x38 +#define DW_OP_lit9 0x39 +#define DW_OP_lit10 0x3a +#define DW_OP_lit11 0x3b +#define DW_OP_lit12 0x3c +#define DW_OP_lit13 0x3d +#define DW_OP_lit14 0x3e +#define DW_OP_lit15 0x3f +#define DW_OP_lit16 0x40 +#define DW_OP_lit17 0x41 +#define DW_OP_lit18 0x42 +#define DW_OP_lit19 0x43 +#define DW_OP_lit20 0x44 +#define DW_OP_lit21 0x45 +#define DW_OP_lit22 0x46 +#define DW_OP_lit23 0x47 +#define DW_OP_lit24 0x48 +#define DW_OP_lit25 0x49 +#define DW_OP_lit26 0x4a +#define DW_OP_lit27 0x4b +#define DW_OP_lit28 0x4c +#define DW_OP_lit29 0x4d +#define DW_OP_lit30 0x4e +#define DW_OP_lit31 0x4f +#define DW_OP_reg0 0x50 +#define DW_OP_reg1 0x51 +#define DW_OP_reg2 0x52 +#define DW_OP_reg3 0x53 +#define DW_OP_reg4 0x54 +#define DW_OP_reg5 0x55 +#define DW_OP_reg6 0x56 +#define DW_OP_reg7 0x57 +#define DW_OP_reg8 0x58 +#define DW_OP_reg9 0x59 +#define DW_OP_reg10 0x5a +#define DW_OP_reg11 0x5b +#define DW_OP_reg12 0x5c +#define DW_OP_reg13 0x5d +#define DW_OP_reg14 0x5e +#define DW_OP_reg15 0x5f +#define DW_OP_reg16 0x60 +#define DW_OP_reg17 0x61 +#define DW_OP_reg18 0x62 +#define DW_OP_reg19 0x63 +#define DW_OP_reg20 0x64 +#define DW_OP_reg21 0x65 +#define DW_OP_reg22 0x66 +#define DW_OP_reg23 0x67 +#define DW_OP_reg24 0x68 +#define DW_OP_reg25 0x69 +#define DW_OP_reg26 0x6a +#define DW_OP_reg27 0x6b +#define DW_OP_reg28 0x6c +#define DW_OP_reg29 0x6d +#define DW_OP_reg30 0x6e +#define DW_OP_reg31 0x6f +#define DW_OP_breg0 0x70 +#define DW_OP_breg1 0x71 +#define DW_OP_breg2 0x72 +#define DW_OP_breg3 0x73 +#define DW_OP_breg4 0x74 +#define DW_OP_breg5 0x75 +#define DW_OP_breg6 0x76 +#define DW_OP_breg7 0x77 +#define DW_OP_breg8 0x78 +#define DW_OP_breg9 0x79 +#define DW_OP_breg10 0x7a +#define DW_OP_breg11 0x7b +#define DW_OP_breg12 0x7c +#define DW_OP_breg13 0x7d +#define DW_OP_breg14 0x7e +#define DW_OP_breg15 0x7f +#define DW_OP_breg16 0x80 +#define DW_OP_breg17 0x81 +#define DW_OP_breg18 0x82 +#define DW_OP_breg19 0x83 +#define DW_OP_breg20 0x84 +#define DW_OP_breg21 0x85 +#define DW_OP_breg22 0x86 +#define DW_OP_breg23 0x87 +#define DW_OP_breg24 0x88 +#define DW_OP_breg25 0x89 +#define DW_OP_breg26 0x8a +#define DW_OP_breg27 0x8b +#define DW_OP_breg28 0x8c +#define DW_OP_breg29 0x8d +#define DW_OP_breg30 0x8e +#define DW_OP_breg31 0x8f +#define DW_OP_regx 0x90 +#define DW_OP_fbreg 0x91 +#define DW_OP_bregx 0x92 +#define DW_OP_piece 0x93 +#define DW_OP_deref_size 0x94 +#define DW_OP_xderef_size 0x95 +#define DW_OP_nop 0x96 +#define DW_OP_push_object_address 0x97 /* DWARF3 */ +#define DW_OP_call2 0x98 /* DWARF3 */ +#define DW_OP_call4 0x99 /* DWARF3 */ +#define DW_OP_call_ref 0x9a /* DWARF3 */ +#define DW_OP_form_tls_address 0x9b /* DWARF3f */ +#define DW_OP_call_frame_cfa 0x9c /* DWARF3f */ +#define DW_OP_bit_piece 0x9d /* DWARF3f */ +#define DW_OP_implicit_value 0x9e /* DWARF4 */ +#define DW_OP_stack_value 0x9f /* DWARF4 */ + + + /* GNU extensions. */ +#define DW_OP_GNU_push_tls_address 0xe0 /* GNU */ + +/* Follows extension so dwarfdump prints the most-likely-useful name. */ +#define DW_OP_lo_user 0xe0 + + /* HP extensions. */ +#define DW_OP_HP_unknown 0xe0 /* HP conflict: GNU */ +#define DW_OP_HP_is_value 0xe1 /* HP */ +#define DW_OP_HP_fltconst4 0xe2 /* HP */ +#define DW_OP_HP_fltconst8 0xe3 /* HP */ +#define DW_OP_HP_mod_range 0xe4 /* HP */ +#define DW_OP_HP_unmod_range 0xe5 /* HP */ +#define DW_OP_HP_tls 0xe6 /* HP */ + +#define DW_OP_INTEL_bit_piece 0xe8 /* Intel: made obsolete by DW_OP_bit_piece above. */ + + + /* Apple extension. */ +#define DW_OP_APPLE_uninit 0xf0 /* Apple */ + +#define DW_OP_hi_user 0xff + +#define DW_ATE_address 0x1 +#define DW_ATE_boolean 0x2 +#define DW_ATE_complex_float 0x3 +#define DW_ATE_float 0x4 +#define DW_ATE_signed 0x5 +#define DW_ATE_signed_char 0x6 +#define DW_ATE_unsigned 0x7 +#define DW_ATE_unsigned_char 0x8 +#define DW_ATE_imaginary_float 0x9 /* DWARF3 */ +#define DW_ATE_packed_decimal 0xa /* DWARF3f */ +#define DW_ATE_numeric_string 0xb /* DWARF3f */ +#define DW_ATE_edited 0xc /* DWARF3f */ +#define DW_ATE_signed_fixed 0xd /* DWARF3f */ +#define DW_ATE_unsigned_fixed 0xe /* DWARF3f */ +#define DW_ATE_decimal_float 0xf /* DWARF3f */ + + +/* ALTIUM extensions. x80, x81 */ +#define DW_ATE_ALTIUM_fract 0x80 /* ALTIUM __fract type */ + +/* Follows extension so dwarfdump prints the most-likely-useful name. */ +#define DW_ATE_lo_user 0x80 + +/* Shown here to help dwarfdump build script. */ +#define DW_ATE_ALTIUM_accum 0x81 /* ALTIUM __accum type */ + +/* HP Floating point extensions. */ +#define DW_ATE_HP_float80 0x80 /* (80 bit). HP */ + + +#define DW_ATE_HP_complex_float80 0x81 /* Complex (80 bit). HP */ +#define DW_ATE_HP_float128 0x82 /* (128 bit). HP */ +#define DW_ATE_HP_complex_float128 0x83 /* Complex (128 bit). HP */ +#define DW_ATE_HP_floathpintel 0x84 /* (82 bit IA64). HP */ +#define DW_ATE_HP_imaginary_float80 0x85 /* HP */ +#define DW_ATE_HP_imaginary_float128 0x86 /* HP */ + +/* Sun extensions */ +#define DW_ATE_SUN_interval_float 0x91 +#define DW_ATE_SUN_imaginary_float 0x92 /* Obsolete: See DW_ATE_imaginary_float */ + +#define DW_ATE_hi_user 0xff + + +/* Decimal Sign codes. */ +#define DW_DS_unsigned 0x01 /* DWARF3f */ +#define DW_DS_leading_overpunch 0x02 /* DWARF3f */ +#define DW_DS_trailing_overpunch 0x03 /* DWARF3f */ +#define DW_DS_leading_separate 0x04 /* DWARF3f */ + +#define DW_DS_trailing_separate 0x05 /* DWARF3f */ + +/* Endian code name. */ +#define DW_END_default 0x00 /* DWARF3f */ +#define DW_END_big 0x01 /* DWARF3f */ +#define DW_END_little 0x02 /* DWARF3f */ + +#define DW_END_lo_user 0x40 /* DWARF3f */ +#define DW_END_hi_user 0xff /* DWARF3f */ + +/* For use with DW_TAG_SUN_codeflags + * If DW_TAG_SUN_codeflags is accepted as a dwarf standard, then + * standard dwarf ATCF entries start at 0x01 + */ +#define DW_ATCF_lo_user 0x40 /* SUN */ +#define DW_ATCF_SUN_mop_bitfield 0x41 /* SUN */ +#define DW_ATCF_SUN_mop_spill 0x42 /* SUN */ +#define DW_ATCF_SUN_mop_scopy 0x43 /* SUN */ +#define DW_ATCF_SUN_func_start 0x44 /* SUN */ +#define DW_ATCF_SUN_end_ctors 0x45 /* SUN */ +#define DW_ATCF_SUN_branch_target 0x46 /* SUN */ +#define DW_ATCF_SUN_mop_stack_probe 0x47 /* SUN */ +#define DW_ATCF_SUN_func_epilog 0x48 /* SUN */ +#define DW_ATCF_hi_user 0xff /* SUN */ + +/* Accessibility code name. */ +#define DW_ACCESS_public 0x01 +#define DW_ACCESS_protected 0x02 +#define DW_ACCESS_private 0x03 + +/* Visibility code name. */ +#define DW_VIS_local 0x01 +#define DW_VIS_exported 0x02 +#define DW_VIS_qualified 0x03 + +/* Virtuality code name. */ +#define DW_VIRTUALITY_none 0x00 +#define DW_VIRTUALITY_virtual 0x01 +#define DW_VIRTUALITY_pure_virtual 0x02 + +#define DW_LANG_C89 0x0001 +#define DW_LANG_C 0x0002 +#define DW_LANG_Ada83 0x0003 +#define DW_LANG_C_plus_plus 0x0004 +#define DW_LANG_Cobol74 0x0005 +#define DW_LANG_Cobol85 0x0006 +#define DW_LANG_Fortran77 0x0007 +#define DW_LANG_Fortran90 0x0008 +#define DW_LANG_Pascal83 0x0009 +#define DW_LANG_Modula2 0x000a +#define DW_LANG_Java 0x000b /* DWARF3 */ +#define DW_LANG_C99 0x000c /* DWARF3 */ +#define DW_LANG_Ada95 0x000d /* DWARF3 */ +#define DW_LANG_Fortran95 0x000e /* DWARF3 */ +#define DW_LANG_PLI 0x000f /* DWARF3 */ +#define DW_LANG_ObjC 0x0010 /* DWARF3f */ +#define DW_LANG_ObjC_plus_plus 0x0011 /* DWARF3f */ +#define DW_LANG_UPC 0x0012 /* DWARF3f */ +#define DW_LANG_D 0x0013 /* DWARF3f */ +#define DW_LANG_Python 0x0014 /* DWARF4 */ +#define DW_LANG_lo_user 0x8000 +#define DW_LANG_Mips_Assembler 0x8001 /* MIPS */ +#define DW_LANG_Upc 0x8765 /* UPC, use + DW_LANG_UPC instead. */ +/* ALTIUM extension */ +#define DW_LANG_ALTIUM_Assembler 0x9101 /* ALTIUM */ + +/* Sun extensions */ +#define DW_LANG_SUN_Assembler 0x9001 /* SUN */ + +#define DW_LANG_hi_user 0xffff + +/* Identifier case name. */ +#define DW_ID_case_sensitive 0x00 +#define DW_ID_up_case 0x01 +#define DW_ID_down_case 0x02 +#define DW_ID_case_insensitive 0x03 + +/* Calling Convention Name. */ +#define DW_CC_normal 0x01 +#define DW_CC_program 0x02 +#define DW_CC_nocall 0x03 +#define DW_CC_lo_user 0x40 + +/* ALTIUM extensions. */ +/* Function is an interrupt handler, return address on system stack. */ +#define DW_CC_ALTIUM_interrupt 0x65 /* ALTIUM*/ + +/* Near function model, return address on system stack. */ +#define DW_CC_ALTIUM_near_system_stack 0x66 /*ALTIUM */ + +/* Near function model, return address on user stack. */ +#define DW_CC_ALTIUM_near_user_stack 0x67 /* ALTIUM */ + +/* Huge function model, return address on user stack. */ +#define DW_CC_ALTIUM_huge_user_stack 0x68 /* ALTIUM */ + + +#define DW_CC_hi_user 0xff + +/* Inline Code Name. */ +#define DW_INL_not_inlined 0x00 +#define DW_INL_inlined 0x01 +#define DW_INL_declared_not_inlined 0x02 +#define DW_INL_declared_inlined 0x03 + +/* Ordering Name. */ +#define DW_ORD_row_major 0x00 +#define DW_ORD_col_major 0x01 + +/* Discriminant Descriptor Name. */ +#define DW_DSC_label 0x00 +#define DW_DSC_range 0x01 + +/* Line number standard opcode name. */ +#define DW_LNS_copy 0x01 +#define DW_LNS_advance_pc 0x02 +#define DW_LNS_advance_line 0x03 +#define DW_LNS_set_file 0x04 +#define DW_LNS_set_column 0x05 +#define DW_LNS_negate_stmt 0x06 +#define DW_LNS_set_basic_block 0x07 +#define DW_LNS_const_add_pc 0x08 +#define DW_LNS_fixed_advance_pc 0x09 +#define DW_LNS_set_prologue_end 0x0a /* DWARF3 */ +#define DW_LNS_set_epilogue_begin 0x0b /* DWARF3 */ +#define DW_LNS_set_isa 0x0c /* DWARF3 */ + +/* Line number extended opcode name. */ +#define DW_LNE_end_sequence 0x01 +#define DW_LNE_set_address 0x02 +#define DW_LNE_define_file 0x03 +#define DW_LNE_set_discriminator 0x04 /* DWARF4 */ + +/* HP extensions. */ +#define DW_LNE_HP_negate_is_UV_update 0x11 /* 17 HP */ +#define DW_LNE_HP_push_context 0x12 /* 18 HP */ +#define DW_LNE_HP_pop_context 0x13 /* 19 HP */ +#define DW_LNE_HP_set_file_line_column 0x14 /* 20 HP */ +#define DW_LNE_HP_set_routine_name 0x15 /* 21 HP */ +#define DW_LNE_HP_set_sequence 0x16 /* 22 HP */ +#define DW_LNE_HP_negate_post_semantics 0x17 /* 23 HP */ +#define DW_LNE_HP_negate_function_exit 0x18 /* 24 HP */ +#define DW_LNE_HP_negate_front_end_logical 0x19 /* 25 HP */ +#define DW_LNE_HP_define_proc 0x20 /* 32 HP */ + +#define DW_LNE_lo_user 0x80 /* DWARF3 */ +#define DW_LNE_hi_user 0xff /* DWARF3 */ + +/* Macro information. */ +#define DW_MACINFO_define 0x01 +#define DW_MACINFO_undef 0x02 +#define DW_MACINFO_start_file 0x03 +#define DW_MACINFO_end_file 0x04 +#define DW_MACINFO_vendor_ext 0xff + +/* CFA operator compaction (a space saving measure, see + the DWARF standard) means DW_CFA_extended and DW_CFA_nop + have the same value here. */ +#define DW_CFA_advance_loc 0x40 +#define DW_CFA_offset 0x80 +#define DW_CFA_restore 0xc0 +#define DW_CFA_extended 0 + +#define DW_CFA_nop 0x00 +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_def_cfa_expression 0x0f /* DWARF3 */ +#define DW_CFA_expression 0x10 /* DWARF3 */ +#define DW_CFA_offset_extended_sf 0x11 /* DWARF3 */ +#define DW_CFA_def_cfa_sf 0x12 /* DWARF3 */ +#define DW_CFA_def_cfa_offset_sf 0x13 /* DWARF3 */ +#define DW_CFA_val_offset 0x14 /* DWARF3f */ +#define DW_CFA_val_offset_sf 0x15 /* DWARF3f */ +#define DW_CFA_val_expression 0x16 /* DWARF3f */ + +#define DW_CFA_lo_user 0x1c +#define DW_CFA_low_user 0x1c /* Incorrect spelling, do not use. */ + +/* SGI/MIPS extension. */ +#define DW_CFA_MIPS_advance_loc8 0x1d /* MIPS */ + +/* GNU extensions. */ +#define DW_CFA_GNU_window_save 0x2d /* GNU */ +#define DW_CFA_GNU_args_size 0x2e /* GNU */ +#define DW_CFA_GNU_negative_offset_extended 0x2f /* GNU */ + +#define DW_CFA_high_user 0x3f + +/* GNU exception header encoding. See the Generic + Elf Specification of the Linux Standard Base (LSB). + http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html + The upper 4 bits indicate how the value is to be applied. + The lower 4 bits indicate the format of the data. +*/ +#define DW_EH_PE_absptr 0x00 /* GNU */ +#define DW_EH_PE_uleb128 0x01 /* GNU */ +#define DW_EH_PE_udata2 0x02 /* GNU */ +#define DW_EH_PE_udata4 0x03 /* GNU */ +#define DW_EH_PE_udata8 0x04 /* GNU */ +#define DW_EH_PE_sleb128 0x09 /* GNU */ +#define DW_EH_PE_sdata2 0x0A /* GNU */ +#define DW_EH_PE_sdata4 0x0B /* GNU */ +#define DW_EH_PE_sdata8 0x0C /* GNU */ + +#define DW_EH_PE_pcrel 0x10 /* GNU */ +#define DW_EH_PE_textrel 0x20 /* GNU */ +#define DW_EH_PE_datarel 0x30 /* GNU */ +#define DW_EH_PE_funcrel 0x40 /* GNU */ +#define DW_EH_PE_aligned 0x50 /* GNU */ + +#define DW_EH_PE_omit 0xff /* GNU. Means no value present. */ + + +/* Mapping from machine registers and pseudo-regs into the .debug_frame table. + DW_FRAME entries are machine specific. These describe + MIPS/SGI R3000, R4K, R4400 and all later MIPS/SGI IRIX machines. + They describe a mapping from hardware register number to + the number used in the table to identify that register. + + The CFA (Canonical Frame Address) described in DWARF is called + the Virtual Frame Pointer on MIPS/SGI machines. + + The DW_FRAME* names here are MIPS/SGI specfic. + Libdwarf interfaces defined in 2008 make the FRAME definitions + here (and the fixed table sizes they imply) obsolete. + They are left here for compatibility. + +*/ +/* Column used for CFA. Assumes reg 0 never appears as + a register in DWARF info. */ +#define DW_FRAME_CFA_COL 0 + +#define DW_FRAME_REG1 1 /* integer reg 1 */ +#define DW_FRAME_REG2 2 /* integer reg 2 */ +#define DW_FRAME_REG3 3 /* integer reg 3 */ +#define DW_FRAME_REG4 4 /* integer reg 4 */ +#define DW_FRAME_REG5 5 /* integer reg 5 */ +#define DW_FRAME_REG6 6 /* integer reg 6 */ +#define DW_FRAME_REG7 7 /* integer reg 7 */ +#define DW_FRAME_REG8 8 /* integer reg 8 */ +#define DW_FRAME_REG9 9 /* integer reg 9 */ +#define DW_FRAME_REG10 10 /* integer reg 10 */ +#define DW_FRAME_REG11 11 /* integer reg 11 */ +#define DW_FRAME_REG12 12 /* integer reg 12 */ +#define DW_FRAME_REG13 13 /* integer reg 13 */ +#define DW_FRAME_REG14 14 /* integer reg 14 */ +#define DW_FRAME_REG15 15 /* integer reg 15 */ +#define DW_FRAME_REG16 16 /* integer reg 16 */ +#define DW_FRAME_REG17 17 /* integer reg 17 */ +#define DW_FRAME_REG18 18 /* integer reg 18 */ +#define DW_FRAME_REG19 19 /* integer reg 19 */ +#define DW_FRAME_REG20 20 /* integer reg 20 */ +#define DW_FRAME_REG21 21 /* integer reg 21 */ +#define DW_FRAME_REG22 22 /* integer reg 22 */ +#define DW_FRAME_REG23 23 /* integer reg 23 */ +#define DW_FRAME_REG24 24 /* integer reg 24 */ +#define DW_FRAME_REG25 25 /* integer reg 25 */ +#define DW_FRAME_REG26 26 /* integer reg 26 */ +#define DW_FRAME_REG27 27 /* integer reg 27 */ +#define DW_FRAME_REG28 28 /* integer reg 28 */ +#define DW_FRAME_REG29 29 /* integer reg 29 */ +#define DW_FRAME_REG30 30 /* integer reg 30 */ +#define DW_FRAME_REG31 31 /* integer reg 31, aka ra */ + + /* MIPS1, 2 have only some of these 64-bit registers. + ** MIPS1 save/restore takes 2 instructions per 64-bit reg, and + ** in that case, the register is considered stored after the second + ** swc1. + */ +#define DW_FRAME_FREG0 32 /* 64-bit floating point reg 0 */ +#define DW_FRAME_FREG1 33 /* 64-bit floating point reg 1 */ +#define DW_FRAME_FREG2 34 /* 64-bit floating point reg 2 */ +#define DW_FRAME_FREG3 35 /* 64-bit floating point reg 3 */ +#define DW_FRAME_FREG4 36 /* 64-bit floating point reg 4 */ +#define DW_FRAME_FREG5 37 /* 64-bit floating point reg 5 */ +#define DW_FRAME_FREG6 38 /* 64-bit floating point reg 6 */ +#define DW_FRAME_FREG7 39 /* 64-bit floating point reg 7 */ +#define DW_FRAME_FREG8 40 /* 64-bit floating point reg 8 */ +#define DW_FRAME_FREG9 41 /* 64-bit floating point reg 9 */ +#define DW_FRAME_FREG10 42 /* 64-bit floating point reg 10 */ +#define DW_FRAME_FREG11 43 /* 64-bit floating point reg 11 */ +#define DW_FRAME_FREG12 44 /* 64-bit floating point reg 12 */ +#define DW_FRAME_FREG13 45 /* 64-bit floating point reg 13 */ +#define DW_FRAME_FREG14 46 /* 64-bit floating point reg 14 */ +#define DW_FRAME_FREG15 47 /* 64-bit floating point reg 15 */ +#define DW_FRAME_FREG16 48 /* 64-bit floating point reg 16 */ +#define DW_FRAME_FREG17 49 /* 64-bit floating point reg 17 */ +#define DW_FRAME_FREG18 50 /* 64-bit floating point reg 18 */ +#define DW_FRAME_FREG19 51 /* 64-bit floating point reg 19 */ +#define DW_FRAME_FREG20 52 /* 64-bit floating point reg 20 */ +#define DW_FRAME_FREG21 53 /* 64-bit floating point reg 21 */ +#define DW_FRAME_FREG22 54 /* 64-bit floating point reg 22 */ +#define DW_FRAME_FREG23 55 /* 64-bit floating point reg 23 */ +#define DW_FRAME_FREG24 56 /* 64-bit floating point reg 24 */ +#define DW_FRAME_FREG25 57 /* 64-bit floating point reg 25 */ +#define DW_FRAME_FREG26 58 /* 64-bit floating point reg 26 */ +#define DW_FRAME_FREG27 59 /* 64-bit floating point reg 27 */ +#define DW_FRAME_FREG28 60 /* 64-bit floating point reg 28 */ +#define DW_FRAME_FREG29 61 /* 64-bit floating point reg 29 */ +#define DW_FRAME_FREG30 62 /* 64-bit floating point reg 30 */ +#define DW_FRAME_FREG31 63 /* 64-bit floating point reg 31 */ + +/* ***IMPORTANT NOTE, TARGET DEPENDENCY **** + The following 4 #defines are dependent on + the target cpu(s) that you apply libdwarf to. + Ensure that DW_FRAME_UNDEFINED_VAL and DW_FRAME_SAME_VAL + do not conflict with the range [0-DW_FRAME_STATIC_LINK]. + The value 63 works for MIPS cpus at least up to the R16000. + + For a cpu with more than 63 real registers + DW_FRAME_HIGHEST_NORMAL_REGISTER + must be increased for things to work properly! + Also ensure that DW_FRAME_UNDEFINED_VAL DW_FRAME_SAME_VAL + are not in the range [0-DW_FRAME_STATIC_LINK] + + Having DW_FRAME_HIGHEST_NORMAL_REGISTER be higher than + is strictly needed is safe. + +*/ + +#ifndef DW_FRAME_HIGHEST_NORMAL_REGISTER +#define DW_FRAME_HIGHEST_NORMAL_REGISTER 63 +#endif +/* This is the number of columns in the Frame Table. + This constant should + be kept in sync with DW_REG_TABLE_SIZE defined in libdwarf.h + It must also be large enough to be beyond the highest + compiler-defined-register (meaning DW_FRAME_RA_COL DW_FRAME_STATIC_LINK + in the MIPS/IRIX case */ +#ifndef DW_FRAME_LAST_REG_NUM +#define DW_FRAME_LAST_REG_NUM (DW_FRAME_HIGHEST_NORMAL_REGISTER + 3) +#endif + + +/* Column recording ra (return addrress from a function call). + This is common to many architectures, but as a 'simple register' + is not necessarily adequate for all architectures. + For MIPS/IRIX this register number is actually recorded on disk + in the .debug_frame section. + */ +#define DW_FRAME_RA_COL (DW_FRAME_HIGHEST_NORMAL_REGISTER + 1) + +/* Column recording static link applicable to up-level + addressing, as in IRIX mp code, pascal, etc. + This is common to many architectures but + is not necessarily adequate for all architectures. + For MIPS/IRIX this register number is actually recorded on disk + in the .debug_frame section. +*/ +#define DW_FRAME_STATIC_LINK (DW_FRAME_HIGHEST_NORMAL_REGISTER + 2) + + + +/* + DW_FRAME_UNDEFINED_VAL and DW_FRAME_SAME_VAL are + never on disk, just generated by libdwarf. See libdwarf.h + for their values. +*/ + + + +#define DW_CHILDREN_no 0x00 +#define DW_CHILDREN_yes 0x01 + +#define DW_ADDR_none 0 + +#ifdef __cplusplus +} +#endif +#endif /* __DWARF_H */ diff --git a/elff/dwarf_cu.cc b/elff/dwarf_cu.cc new file mode 100644 index 0000000..8e7da98 --- /dev/null +++ b/elff/dwarf_cu.cc @@ -0,0 +1,758 @@ +/* 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 a class DwarfCU, that encapsulates a compilation
+ * unit in the .debug_info section of the mapped ELF file.
+ */
+
+#include "string.h"
+#include "stdio.h"
+#include "elf_file.h"
+#include "dwarf_cu.h"
+#include "dwarf_utils.h"
+
+DwarfCU::DwarfCU(ElfFile* elf)
+ : elf_file_(elf),
+ cu_die_(NULL),
+ prev_cu_(NULL) {
+}
+
+DwarfCU::~DwarfCU() {
+ if (cu_die_ != NULL) {
+ delete cu_die_;
+ }
+ abbrs_.empty();
+}
+
+DwarfCU* DwarfCU::create_instance(ElfFile* elf, const void* hdr) {
+ DwarfCU* ret;
+
+ /* 64-bit DWARF CU has first 4 bytes in its header set to 0xFFFFFFFF. */
+ if (*reinterpret_cast<const Elf_Word*>(hdr) == 0xFFFFFFFF) {
+ ret = new(elf) DwarfCUImpl<Dwarf64_CUHdr, Dwarf64_Off>
+ (elf, reinterpret_cast<const Dwarf64_CUHdr*>(hdr));
+ } else {
+ ret = new(elf) DwarfCUImpl<Dwarf32_CUHdr, Dwarf32_Off>
+ (elf, reinterpret_cast<const Dwarf32_CUHdr*>(hdr));
+ }
+ assert(ret != NULL);
+ if (ret == NULL) {
+ _set_errno(ENOMEM);
+ }
+ return ret;
+}
+
+const Elf_Byte* DwarfCU::process_attrib(const Elf_Byte* prop,
+ Dwarf_Form form,
+ Dwarf_Value* attr_value) const {
+ assert(form != 0);
+ Dwarf_Value tmp_val;
+ Dwarf_Value leb128;
+
+ attr_value->type = DWARF_VALUE_UNKNOWN;
+ attr_value->encoded_size = 0;
+ attr_value->u64 = 0;
+
+ switch (form) {
+ /* Property is a block of data, contained in .debug_info section. Block
+ * size is encoded with 1 byte value, and block data immediately follows
+ * block size. */
+ case DW_FORM_block1:
+ attr_value->type = DWARF_VALUE_BLOCK;
+ attr_value->block.block_size = *prop;
+ attr_value->block.block_ptr = prop + 1;
+ attr_value->encoded_size =
+ static_cast<Elf_Word>(attr_value->block.block_size + 1);
+ break;
+
+ /* Property is a block of data, contained in .debug_info section. Block
+ * size is encoded with 2 bytes value, and block data immediately follows
+ * block size. */
+ case DW_FORM_block2:
+ attr_value->type = DWARF_VALUE_BLOCK;
+ attr_value->block.block_size =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Half*>(prop));
+ attr_value->block.block_ptr = prop + 2;
+ attr_value->encoded_size =
+ static_cast<Elf_Word>(attr_value->block.block_size + 2);
+ break;
+
+ /* Property is a block of data, contained in .debug_info section. Block
+ * size is encoded with 4 bytes value, and block data immediately follows
+ * block size. */
+ case DW_FORM_block4:
+ attr_value->type = DWARF_VALUE_BLOCK;
+ attr_value->block.block_size =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop));
+ attr_value->block.block_ptr = prop + 4;
+ attr_value->encoded_size =
+ static_cast<Elf_Word>(attr_value->block.block_size + 4);
+ break;
+
+ /* Property is a block of data, contained in .debug_info section. Block
+ * size is encoded with unsigned LEB128 value, and block data immediately
+ * follows block size. */
+ case DW_FORM_block:
+ reinterpret_cast<const Dwarf_Leb128*>(prop)->process_unsigned(&leb128);
+ attr_value->type = DWARF_VALUE_BLOCK;
+ attr_value->block.block_size = leb128.u32;
+ attr_value->block.block_ptr = prop + leb128.encoded_size;
+ attr_value->encoded_size =
+ static_cast<Elf_Word>(attr_value->block.block_size +
+ leb128.encoded_size);
+ break;
+
+ /* Property is unsigned 1 byte value. */
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ case DW_FORM_ref1:
+ attr_value->type = DWARF_VALUE_U8;
+ attr_value->u8 = *prop;
+ attr_value->encoded_size = 1;
+ break;
+
+ /* Property is unsigned 2 bytes value. */
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ attr_value->type = DWARF_VALUE_U16;
+ attr_value->u16 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Half*>(prop));
+ attr_value->encoded_size = 2;
+ break;
+
+ /* Property is unsigned 4 bytes value. */
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ attr_value->type = DWARF_VALUE_U32;
+ attr_value->u32 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop));
+ attr_value->encoded_size = 4;
+ break;
+
+ /* Property is unsigned 8 bytes value. */
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_sig8:
+ attr_value->type = DWARF_VALUE_U64;
+ attr_value->u64 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop));
+ attr_value->encoded_size = 8;
+ break;
+
+ /* Property is signed LEB128 value. */
+ case DW_FORM_sdata:
+ reinterpret_cast<const Dwarf_Leb128*>(prop)->process_signed(attr_value);
+ break;
+
+ /* Property is unsigned LEB128 value. */
+ case DW_FORM_ref_udata:
+ case DW_FORM_udata:
+ reinterpret_cast<const Dwarf_Leb128*>(prop)->process_unsigned(attr_value);
+ break;
+
+ /* Property is a string contained directly in .debug_info section. */
+ case DW_FORM_string:
+ attr_value->type = DWARF_VALUE_STR;
+ attr_value->str = reinterpret_cast<const char*>(prop);
+ attr_value->encoded_size = strlen(attr_value->str) + 1;
+ break;
+
+ /* Property is an offset of a string contained in .debug_str section.
+ * We will process the reference here, converting it into the actual
+ * string value. */
+ case DW_FORM_strp:
+ attr_value->type = DWARF_VALUE_STR;
+ if (elf_file_->is_DWARF_64()) {
+ Elf_Xword str_offset =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop));
+ attr_value->str = elf_file_->get_debug_str(str_offset);
+ attr_value->encoded_size = 8;
+ } else {
+ Elf_Word str_offset =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop));
+ attr_value->str = elf_file_->get_debug_str(str_offset);
+ attr_value->encoded_size = 4;
+ }
+ break;
+
+ /* Property is an address. */
+ case DW_FORM_addr:
+ if (addr_sizeof_ == 4) {
+ attr_value->type = DWARF_VALUE_PTR32;
+ attr_value->u32 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop));
+ } else {
+ attr_value->type = DWARF_VALUE_PTR64;
+ attr_value->u64 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop));
+ }
+ attr_value->encoded_size = addr_sizeof_;
+ break;
+
+ /* Reference from the beginning of .debug_info section. */
+ case DW_FORM_ref_addr:
+ /* DWARF3+ requires that encoding size of this property must be 4 bytes
+ * in 32-bit DWARF, and 8 bytes in 64-bit DWARF, while DWARF2- requires
+ * encoding size to be equal to CU's pointer size. */
+ if (is_DWARF3_or_higher()) {
+ if (elf_file_->is_DWARF_64()) {
+ attr_value->type = DWARF_VALUE_U64;
+ attr_value->u64 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop));
+ attr_value->encoded_size = 4;
+ } else {
+ attr_value->type = DWARF_VALUE_U32;
+ attr_value->u32 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop));
+ attr_value->encoded_size = 8;
+ }
+ } else {
+ if (addr_sizeof_ == 4) {
+ attr_value->type = DWARF_VALUE_U32;
+ attr_value->u32 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop));
+ } else {
+ attr_value->type = DWARF_VALUE_U64;
+ attr_value->u64 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop));
+ }
+ attr_value->encoded_size = addr_sizeof_;
+ }
+ break;
+
+ /* Reference to a section, other than .debug_info, or .debug_str */
+ case DW_FORM_sec_offset:
+ if (elf_file_->is_DWARF_64()) {
+ attr_value->type = DWARF_VALUE_U64;
+ attr_value->u64 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop));
+ attr_value->encoded_size = 4;
+ } else {
+ attr_value->type = DWARF_VALUE_U32;
+ attr_value->u32 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop));
+ attr_value->encoded_size = 8;
+ }
+ break;
+
+ /* This is a replacement for DW_FORM_flag, which doesn't consume memory
+ * in .debug_info section, and only by the fact of its existence it is
+ * equal to DW_FORM_flag with value set to 1. */
+ case DW_FORM_flag_present:
+ attr_value->type = DWARF_VALUE_U8;
+ attr_value->u8 = 1;
+ attr_value->encoded_size = 0;
+ break;
+
+ /* Encodes the actual form to be used. */
+ case DW_FORM_indirect:
+ // Starts with ULEB128
+ prop = reinterpret_cast<const Elf_Byte*>
+ (reinterpret_cast<const Dwarf_Leb128*>
+ (prop)->process_unsigned(&tmp_val));
+ /* ULEB128 encodes the actual form to be used to process this entry. */
+ process_attrib(prop, tmp_val.u16, attr_value);
+ attr_value->encoded_size += tmp_val.encoded_size;
+ break;
+
+ /* This form is defined for DWARF4, and has no documentation whatsoever. */
+ case DW_FORM_exprloc:
+ default:
+ attr_value->type = DWARF_VALUE_U32;
+ attr_value->u32 =
+ elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop));
+ attr_value->encoded_size = 4;
+ break;
+ }
+
+ return prop + attr_value->encoded_size;
+}
+
+void DwarfCU::dump() const {
+ printf("\n\n>>>>>>>>>>>>>>> CU %p (version %u, address size %u)\n",
+ cu_die_->die(), static_cast<Elf_Word>(version_),
+ static_cast<Elf_Word>(addr_sizeof_));
+ printf(">>>>> Build dir path: %s\n", comp_dir_path());
+ printf(">>>>> Build file path: %s\n", rel_cu_path());
+ if (cu_die_ != NULL) {
+ cu_die_->dump(false);
+ }
+}
+
+//=============================================================================
+// DwarfCUImpl implementation
+//=============================================================================
+
+template <typename Dwarf_CUHdr, typename Dwarf_Off>
+DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::DwarfCUImpl(ElfFile* elf,
+ const Dwarf_CUHdr* hdr)
+ : DwarfCU(elf),
+ cu_header_(hdr) {
+ /* Cache CU's DIE abbreviation descriptor in the array. This MUST be done
+ * BEFORE first call to array's cache_to() method. */
+ const Dwarf_Abbr_DIE* cu_abbr_die = reinterpret_cast<const Dwarf_Abbr_DIE*>
+ (INC_CPTR(elf->get_debug_abbrev_data(),
+ elf->pull_val(hdr->abbrev_offset)));
+ abbrs_.add(cu_abbr_die);
+
+ cu_size_ = elf->pull_val(hdr->size_hdr.size);
+ version_ = elf->pull_val(hdr->version);
+ addr_sizeof_ = hdr->address_size;
+ memset(&stmtl_header_, 0, sizeof(stmtl_header_));
+}
+
+template <typename Dwarf_CUHdr, typename Dwarf_Off>
+bool DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::parse(
+ const DwarfParseContext* parse_context,
+ const void** next_cu_die) {
+ /* Start parsing with the DIE for this CU. */
+ if (process_DIE(parse_context, get_DIE(), NULL) == NULL) {
+ return false;
+ }
+
+ /* CU area size (thus, next CU header offset) in .debug_info section equals
+ * to CU size, plus number of bytes, required to encode CU size in CU header
+ * (4 for 32-bit CU, and 12 for 64-bit CU. */
+ *next_cu_die =
+ INC_CPTR(cu_header_, cu_size_ + ELFF_FIELD_OFFSET(Dwarf_CUHdr, version));
+
+ return true;
+}
+
+template <typename Dwarf_CUHdr, typename Dwarf_Off>
+const Elf_Byte* DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::process_DIE(
+ const DwarfParseContext* parse_context,
+ const Dwarf_DIE* die,
+ DIEObject* parent_obj) {
+ while (is_attrib_ptr_valid(die) && !die->is_separator()) {
+ Dwarf_AbbrNum abbr_num;
+ Dwarf_Tag die_tag;
+ Elf_Word sibling_off = 0;
+
+ /* Get DIE's abbreviation number, and advance to DIE's properties. */
+ const Elf_Byte* die_attr = die->process(&abbr_num);
+
+ /* Get abbreviation for the current DIE. */
+ const Dwarf_Abbr_DIE* die_abbr = abbrs_.cache_to(abbr_num);
+ if (die_abbr == NULL) {
+ return NULL;
+ }
+
+ /* Get base DIE properties, and advance to the DIE's
+ * attribute descriptors. */
+ const Dwarf_Abbr_AT* at_abbr = die_abbr->process(NULL, &die_tag);
+
+ /* Instantiate DIE object for this DIE, and get list of properties,
+ * that should be collected while processing that DIE. */
+ DIEObject* die_obj =
+ create_die_object(parse_context, die, parent_obj, die_tag);
+ if (die_obj == NULL && errno != 0) {
+ return NULL;
+ }
+
+ if (die_obj != NULL) {
+ if (parent_obj != NULL) {
+ /* Update list of parent's children. */
+ die_obj->link_sibling(parent_obj->last_child());
+ parent_obj->link_child(die_obj);
+ } else {
+ /* NULL parent object is allowed only for CU DIE itself. */
+ assert(cu_die_ == NULL && die_tag == DW_TAG_compile_unit);
+ if (cu_die_ == NULL && die_tag != DW_TAG_compile_unit) {
+ _set_errno(EINVAL);
+ return NULL;
+ }
+ cu_die_ = die_obj;
+ /* This CU DIE object will be used as a parent for all DIE
+ * objects, created in this method. */
+ parent_obj = cu_die_;
+ }
+ }
+
+ // Loop through all DIE properties.
+ while (elf_file_->is_valid_abbr_ptr(at_abbr, sizeof(Dwarf_Abbr_AT)) &&
+ !at_abbr->is_separator()) {
+ Dwarf_At at_value;
+ Dwarf_Form at_form;
+ Dwarf_Value attr_value;
+
+ // Obtain next property value.
+ at_abbr = at_abbr->process(&at_value, &at_form);
+ die_attr = process_attrib(die_attr, at_form, &attr_value);
+
+ if (at_value == DW_AT_sibling) {
+ /* DW_AT_sibling means that next DIE is a child of the one that's
+ * being currently processed. We need to cache value of this property
+ * in order to correctly calculate next sibling of this DIE after
+ * child's DIE has been processed. */
+ assert(sibling_off == 0);
+ sibling_off = attr_value.u32;
+ }
+ }
+
+ /* Next DIE immediately follows last property for the current DIE. */
+ die = reinterpret_cast<const Dwarf_DIE*>(die_attr);
+ if (sibling_off != 0) {
+ // Process child DIE.
+ process_DIE(parse_context, die, die_obj != NULL ? die_obj : parent_obj);
+ // Next sibling DIE offset is relative to this CU's header beginning.
+ die = INC_CPTR_T(Dwarf_DIE, cu_header_, sibling_off);
+ }
+ }
+
+ return INC_CPTR_T(Elf_Byte, die, 1);
+}
+
+template <typename Dwarf_CUHdr, typename Dwarf_Off>
+DIEObject* DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::create_die_object(
+ const DwarfParseContext* parse_context,
+ const Dwarf_DIE* die,
+ DIEObject* parent,
+ Dwarf_Tag tag) {
+ DIEObject* ret = NULL;
+
+ /* We will always create a DIE object for CU DIE. */
+ if (tag == DW_TAG_compile_unit || collect_die(parse_context, tag)) {
+ ret = new(elf_file_) DIEObject(die, this, parent);
+ assert(ret != NULL);
+ if (ret == NULL) {
+ _set_errno(ENOMEM);
+ }
+ } else {
+ _set_errno(0);
+ }
+ return ret;
+}
+
+template <typename Dwarf_CUHdr, typename Dwarf_Off>
+bool DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::init_stmtl() {
+ if (stmtl_header_.unit_length != 0) {
+ return true;
+ }
+
+ assert(cu_die_ != NULL);
+ if (cu_die_ == NULL) {
+ _set_errno(EINVAL);
+ return false;
+ }
+
+ DIEAttrib stmtl;
+ if (!cu_die()->get_attrib(DW_AT_stmt_list, &stmtl)) {
+ _set_errno(EINVAL);
+ return false;
+ }
+
+ const void* stmtl_start =
+ INC_CPTR(elf_file()->get_debug_line_data(), stmtl.value()->u32);
+ if (*reinterpret_cast<const Elf_Word*>(stmtl_start) == 0xFFFFFFFF) {
+ cache_stmtl<Dwarf64_STMTLHdr>(reinterpret_cast<const Dwarf64_STMTLHdr*>(stmtl_start));
+ } else {
+ cache_stmtl<Dwarf32_STMTLHdr>(reinterpret_cast<const Dwarf32_STMTLHdr*>(stmtl_start));
+ }
+
+ return true;
+}
+
+template <typename Dwarf_CUHdr, typename Dwarf_Off>
+bool DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::get_pc_address_file_info(
+ Elf_Xword address,
+ Dwarf_AddressInfo* info) {
+ /* Make sure STMTL header is cached. */
+ if (!init_stmtl()) {
+ return false;
+ }
+ /* Flags address match, that should trigger return next time
+ * source line gets adjusted. */
+ bool found = false;
+ /* Create new state machine. */
+ DwarfStateMachine state(stmtl_header_.default_is_stmt != 0);
+
+ /* Start the "Line Number Program" */
+ const Elf_Byte* go = stmtl_header_.start;
+ while (go < stmtl_header_.end) {
+ const Elf_Byte op = *go;
+ go++;
+
+ if (op == 0) {
+ /* This is an extended opcode. */
+ Dwarf_Value op_size;
+
+ /* First ULEB128 contains opcode size, (excluding ULEB128 itself). */
+ go = reinterpret_cast<const Elf_Byte*>
+ (reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&op_size));
+ /* Next is the extended opcode. */
+ const Elf_Byte* ex_op_ptr = go;
+ switch (*ex_op_ptr) {
+ case DW_LNE_end_sequence:
+ state.end_sequence_ = true;
+ state.reset(stmtl_header_.default_is_stmt != 0);
+ found = false;
+ break;
+
+ case DW_LNE_set_address: {
+ Elf_Xword prev_address = state.address_;
+ if (is_CU_address_64()) {
+ state.address_ =
+ elf_file()->pull_val(reinterpret_cast<const Elf_Xword*>(ex_op_ptr + 1));
+ } else {
+ state.address_ =
+ elf_file()->pull_val(reinterpret_cast<const Elf_Word*>(ex_op_ptr + 1));
+ }
+ if (prev_address != 0 &&
+ address >= prev_address && address < state.address_) {
+ return set_source_info(&state, info);
+ } else if (address == state.address_) {
+ found = true;
+ }
+ break;
+ }
+
+ case DW_LNE_define_file: {
+ /* Parameters start with the directly encoded zero-terminated
+ * file name. */
+ state.set_file_info_ = INC_CPTR_T(Dwarf_STMTL_FileDesc, ex_op_ptr, 1);
+ assert(state.set_file_info_ != NULL);
+ if (state.set_file_info_ != NULL) {
+ ex_op_ptr = reinterpret_cast<const Elf_Byte*>(state.set_file_info_->process(NULL));
+ }
+ break;
+ }
+
+ case DW_LNE_set_discriminator: {
+ Dwarf_Value discr_val;
+ /* One parameter: discriminator's ULEB128 value. */
+ reinterpret_cast<const Dwarf_Leb128*>(ex_op_ptr + 1)->process_unsigned(&discr_val);
+ state.discriminator_ = discr_val.u32;
+ break;
+ }
+
+ default:
+ assert(0);
+ return false;
+ }
+ go += op_size.u32;
+ } else if (op < stmtl_header_.opcode_base) {
+ /* This is a standard opcode. */
+ switch (op) {
+ case DW_LNS_copy:
+ /* No parameters. */
+ state.basic_block_ = false;
+ state.prologue_end_ = false;
+ state.epilogue_begin_ = false;
+ break;
+
+ case DW_LNS_advance_pc: {
+ /* One parameter: ULEB128 value to add to the current address value
+ * in the state machine. */
+ Dwarf_Value addr_add;
+ go = reinterpret_cast<const Elf_Byte*>
+ (reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&addr_add));
+ Elf_Xword prev_address = state.address_;
+ state.address_ += addr_add.u64;
+ if (prev_address != 0 &&
+ address >= prev_address && address < state.address_) {
+ return set_source_info(&state, info);
+ } else if (address == state.address_) {
+ found = true;
+ }
+ break;
+ }
+
+ case DW_LNS_advance_line: {
+ /* One parameter: signed LEB128 value to add to the current line
+ * number in the state machine. */
+ Dwarf_Value line_add;
+ go = reinterpret_cast<const Elf_Byte*>
+ (reinterpret_cast<const Dwarf_Leb128*>(go)->process_signed(&line_add));
+ state.line_ += line_add.s32;
+ if (found) {
+ return set_source_info(&state, info);
+ }
+ break;
+ }
+
+ case DW_LNS_set_file: {
+ /* One parameter: ULEB128 value encoding current file number. */
+ Dwarf_Value file_num;
+ go = reinterpret_cast<const Elf_Byte*>
+ (reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&file_num));
+ state.file_ = file_num.u32;
+ /* This operation should discard previously saved file information. */
+ state.set_file_info_ = NULL;
+ break;
+ }
+
+ case DW_LNS_set_column: {
+ /* One parameter: ULEB128 value encoding current column number. */
+ Dwarf_Value column_num;
+ go = reinterpret_cast<const Elf_Byte*>
+ (reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&column_num));
+ state.column_ = column_num.u32;
+ break;
+ }
+
+ case DW_LNS_negate_stmt:
+ /* No parameters. */
+ state.is_stmt_ = !state.is_stmt_;
+ break;
+
+ case DW_LNS_set_basic_block:
+ /* No parameters. */
+ state.basic_block_ = true;
+ break;
+
+ case DW_LNS_const_add_pc: {
+ Elf_Xword prev_address = state.address_;
+ /* No parameters. This operation does the same thing, as special
+ * opcode 255 would do to the current address. */
+ Elf_Word adjusted =
+ static_cast<Elf_Word>(255) - stmtl_header_.opcode_base;
+ state.address_ += (adjusted / stmtl_header_.line_range) *
+ stmtl_header_.min_instruction_len;
+ if (prev_address != 0 &&
+ address >= prev_address && address < state.address_) {
+ return set_source_info(&state, info);
+ } else if (address == state.address_) {
+ found = true;
+ }
+ break;
+ }
+
+ case DW_LNS_fixed_advance_pc: {
+ Elf_Xword prev_address = state.address_;
+ /* One parameter: directly encoded 16-bit value to add to the
+ * current address. */
+ state.address_ +=
+ elf_file()->pull_val(reinterpret_cast<const Elf_Half*>(go));
+ if (prev_address != 0 &&
+ address >= prev_address && address < state.address_) {
+ return set_source_info(&state, info);
+ } else if (address == state.address_) {
+ found = true;
+ }
+ go += sizeof(Elf_Half);
+ break;
+ }
+
+ case DW_LNS_set_prologue_end:
+ /* No parameters. */
+ state.prologue_end_ = true;
+ break;
+
+ case DW_LNS_set_epilogue_begin:
+ /* No parameters. */
+ state.epilogue_begin_ = true;
+ break;
+
+ case DW_LNS_set_isa: {
+ /* One parameter: ISA value encoded as ULEB128. */
+ Dwarf_Value isa_val;
+ go = reinterpret_cast<const Elf_Byte*>
+ (reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&isa_val));
+ state.isa_ = isa_val.u32;
+ break;
+ }
+
+ default:
+ /* Unknown opcode. Just skip it. */
+ for (Elf_Byte uleb = 0;
+ uleb < stmtl_header_.standard_opcode_lengths[op - 1]; uleb++) {
+ Dwarf_Value tmp;
+ go = reinterpret_cast<const Elf_Byte*>
+ (reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&tmp));
+ }
+ break;
+ }
+ } else {
+ Elf_Xword prev_address = state.address_;
+ /* This is a special opcode. */
+ const Elf_Word adjusted = op - stmtl_header_.opcode_base;
+ /* Advance address. */
+ state.address_ += (adjusted / stmtl_header_.line_range) *
+ stmtl_header_.min_instruction_len;
+ if (prev_address != 0 &&
+ address >= prev_address && address < state.address_) {
+ return set_source_info(&state, info);
+ }
+ /* Advance line. */
+ state.line_ += stmtl_header_.line_base +
+ (adjusted % stmtl_header_.line_range);
+ if (state.address_ == address) {
+ return set_source_info(&state, info);
+ }
+ /* Do the woodoo. */
+ state.basic_block_ = false;
+ state.prologue_end_ = false;
+ state.epilogue_begin_ = false;
+ }
+ }
+
+ return false;
+}
+
+template <typename Dwarf_CUHdr, typename Dwarf_Off>
+const Dwarf_STMTL_FileDesc* DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::get_stmt_file_info(
+ Elf_Word index) {
+ /* Index must be 1-based. */
+ if (index == 0) {
+ return NULL;
+ }
+
+ const Dwarf_STMTL_FileDesc* cur_desc = stmtl_header_.file_infos;
+ while (index != 1 && !cur_desc->is_last_entry()) {
+ cur_desc = cur_desc->process(NULL);
+ index--;
+ }
+ assert(!cur_desc->is_last_entry());
+ return cur_desc->is_last_entry() ? NULL : cur_desc;
+}
+
+template <typename Dwarf_CUHdr, typename Dwarf_Off>
+const char* DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::get_stmt_dir_name(
+ Elf_Word dir_index) {
+ if (dir_index == 0) {
+ /* Requested is current compilation directory. */
+ return comp_dir_path();
+ }
+ if (dir_index > stmtl_header_.inc_dir_num) {
+ return NULL;
+ }
+
+ const char* cur_dir = stmtl_header_.include_directories;
+ while (dir_index != 1) {
+ cur_dir += strlen(cur_dir) + 1;
+ dir_index--;
+ }
+ return cur_dir;
+}
+
+template <typename Dwarf_CUHdr, typename Dwarf_Off>
+bool DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::set_source_info(
+ const DwarfStateMachine* state,
+ Dwarf_AddressInfo* info) {
+ info->line_number = state->line_;
+ const Dwarf_STMTL_FileDesc* file_info = state->set_file_info_;
+ if (file_info == NULL) {
+ file_info = get_stmt_file_info(state->file_);
+ if (file_info == NULL) {
+ info->file_name = rel_cu_path();
+ info->dir_name = comp_dir_path();
+ return true;
+ }
+ }
+ info->file_name = file_info->get_file_name();
+ const Elf_Word dir_index = file_info->get_dir_index();
+ info->dir_name = get_stmt_dir_name(dir_index);
+ return true;
+}
+
diff --git a/elff/dwarf_cu.h b/elff/dwarf_cu.h new file mode 100644 index 0000000..a8f0578 --- /dev/null +++ b/elff/dwarf_cu.h @@ -0,0 +1,513 @@ +/* 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 declaration of a class DwarfCU, that encapsulates a compilation
+ * unit in the .debug_info section of the mapped ELF file.
+ */
+
+#ifndef ELFF_DWARF_CU_H_
+#define ELFF_DWARF_CU_H_
+
+#include "dwarf_defs.h"
+#include "dwarf_die.h"
+
+/* Address information descriptor. */
+typedef struct Dwarf_AddressInfo {
+ /* Routine DIE containing the address. */
+ const DIEObject* die_obj;
+
+ /* Source file name for the address. */
+ const char* file_name;
+
+ /* Source file directory path for the address. */
+ const char* dir_name;
+
+ /* Source file line number for the address. */
+ Elf_Word line_number;
+} Dwarf_AddressInfo;
+
+/* STMTL header cached by compilation unit. This header is contained in
+ * the .debug_line section of the ELF file. */
+typedef struct Dwarf_STMTL_Hdr {
+ /* The size in bytes of the line number information for this compilation
+ * unit, not including the unit_length field itself. */
+ Elf_Xword unit_length;
+
+ /* A version number. This number is specific to the line number information
+ * and is independent of the DWARF version number. */
+ Elf_Half version;
+
+ /* The number of bytes following the header_length field to the beginning of
+ * the first byte of the line number program itself. In the 32-bit DWARF
+ * format, this is a 4-byte unsigned length; in the 64-bit DWARF format,
+ * this field is an 8-byte unsigned length. */
+ Elf_Xword header_length;
+
+ /* The size in bytes of the smallest target machine instruction. Line number
+ * program opcodes that alter the address register first multiply their
+ * operands by this value. */
+ Elf_Byte min_instruction_len;
+
+ /* The initial value of the is_stmt register. */
+ Elf_Byte default_is_stmt;
+
+ /* This parameter affects the meaning of the special opcodes. */
+ Elf_Sbyte line_base;
+
+ /* This parameter affects the meaning of the special opcodes. */
+ Elf_Byte line_range;
+
+ /* The number assigned to the first special opcode. */
+ Elf_Byte opcode_base;
+
+ /* Points to standard_opcode_lengths array in the actual STMTL header in
+ * the mapped .debug_line section. */
+ const Elf_Byte* standard_opcode_lengths;
+
+ /* Pointer to the beginning of the list of include directories in the mapped
+ * .debug_line section. */
+ const char* include_directories;
+
+ /* Number of include directories in the list that begins with
+ * include_directories. */
+ Elf_Word inc_dir_num;
+
+ /* Pointer to the beginning of the list of file information in the mapped
+ * .debug_line section. Each entry in this list begins with zero-terminated
+ * file name, followed by ULEB128 encoding directory index for the file,
+ * followed by ULEB128 encoding last modification time, followed by ULEB128
+ * encoding length of file in bytes. */
+ const Dwarf_STMTL_FileDesc* file_infos;
+
+ /* Start of the "Line Number Program" in the mapped .debug_line section. */
+ const Elf_Byte* start;
+
+ /* End of the "Line Number Program" in the mapped .debug_line section. */
+ const Elf_Byte* end;
+} Dwarf_STMTL_Hdr;
+
+/* Encapsulates architecture-independent functionality of a
+ * compilation unit.
+ */
+class DwarfCU : public DwarfAllocBase {
+friend class ElfFile;
+ public:
+ /* Constructs DwarfCU instance.
+ * Param:
+ * elf - Instance of ElfFile containing this compilation unit.
+ */
+ explicit DwarfCU(ElfFile* elf);
+
+ /* Destructs DwarfCU instance. */
+ virtual ~DwarfCU();
+
+ /* Creates DwarfCUImpl instance, depending on DWARF format.
+ * Param:
+ * elf - Instance of ElfFile containing this compilation unit.
+ * hdr - Pointer to compilation unit header inside mapped .debug_info
+ * section of the ELF file. Actual data addressed by this pointer
+ * must be Dwarf32_CUHdr for 32 bit DWARFs, or Dwarf64_CUHdr for
+ * 64 bit DWARFs.
+ * Return:
+ * Created DwarfCUImpl instance (typecasted back to DwarfCU) on success,
+ * or NULL on failure.
+ */
+ static DwarfCU* create_instance(ElfFile* elf, const void* hdr);
+
+ /* Process a DIE attribute.
+ * Param:
+ * attr - Attribute list inside the mapped .debug_info section of the ELF
+ * file.
+ * form - Attribute's form, definig representation of attribute value in the
+ * mapped .debug_info section of the ELF file.
+ * attr_value - Upon return contains attribute value.
+ * Return:
+ * Pointer to the next DIE attribute inside the mapped .debug_info section
+ * of the ELF file.
+ */
+ const Elf_Byte* process_attrib(const Elf_Byte* attr,
+ Dwarf_Form form,
+ Dwarf_Value* attr_value) const;
+
+ /* Dumps this compilation unit to the stdout. */
+ void dump() const;
+
+ /* Gets instance of ElfFile containing this compilation unit. */
+ ElfFile* elf_file() const {
+ return elf_file_;
+ }
+
+ /* Gets DIE object for this CU. */
+ DIEObject* cu_die() const {
+ return cu_die_;
+ }
+
+ /* Gets byte size of the pointer type for this compilation unit. */
+ Elf_Byte addr_sizeof() const {
+ return addr_sizeof_;
+ }
+
+ /* Gets full path to the compilation directory (DW_AT_comp_dir attribute).
+ * Return:
+ * Full path to the compilation directory (DW_AT_comp_dir attribute),
+ * or NULL, if that attribute was missing in CU's attribute list.
+ */
+ const char* comp_dir_path() const {
+ DIEAttrib attr;
+ return cu_die()->get_attrib(DW_AT_comp_dir, &attr) ? attr.value()->str :
+ NULL;
+ }
+
+ /* Gets relative (from the compilation directory) path to the compiled file.
+ * (DW_AT_name attribute).
+ * Return:
+ * Relative path to the compiled file (DW_AT_name attribute), or NULL, if
+ * that attribute was missing in CU's attribute list.
+ */
+ const char* rel_cu_path() const {
+ DIEAttrib attr;
+ return cu_die()->get_attrib(DW_AT_name, &attr) ? attr.value()->str :
+ NULL;
+ }
+
+ /* Gets next compilation unit in the list. NULL indicates the last CU. */
+ DwarfCU* prev_cu() const {
+ return prev_cu_;
+ }
+
+ /* Links this CU to the list of prevoiusly discovered CUs. */
+ void set_prev_cu(DwarfCU* prev) {
+ prev_cu_ = prev;
+ }
+
+ /* Checks if DWARF version for this CU is higher than 2. */
+ bool is_DWARF3_or_higher() const {
+ return version_ >= 3;
+ }
+
+ /* Gets DIE abbreviation for given abbreviation number.
+ * See DwarfAbbrDieArray::get() */
+ const Dwarf_Abbr_DIE* get_die_abbr(Dwarf_AbbrNum num) const {
+ return abbrs_.get(num);
+ }
+
+ /* Gets DIE object containing given address.
+ * DIE address ranges may overlap (for instance, address range for an inlined
+ * routine will be contained within the address range of a routine where it
+ * was inlined). This method will return a DIE object that is a "leaf" in
+ * that inlining tree. I.e the returned DIE object represents the last DIE
+ * in the branch of all DIEs containing given address.
+ * Param:
+ * address - Address to get a DIE for. NOTE: for the sake of simplicity we
+ * explicitly use 64-bit type for an address.
+ * Return:
+ * Leaf DIE containing given address, or NULL if this CU doesn't contain
+ * the given address.
+ */
+ DIEObject* get_leaf_die_for_address(Elf_Xword address) const {
+ return cu_die_->get_leaf_for_address(address);
+ }
+
+ /* Checks if this CU contains 64, or 32-bit addresses. */
+ bool is_CU_address_64() const {
+ return addr_sizeof_ == 8;
+ }
+ bool is_CU_address_32() const {
+ return addr_sizeof_ == 4;
+ }
+
+//=============================================================================
+// DWARF format dependent methods
+//=============================================================================
+
+ public:
+ /* Parses this compilation unit in .debug_info section, collecting children
+ * DIEs of this compilation unit.
+ * Param:
+ * parse_context - Parsing context that lists tags for DIEs that should be
+ * collected during parsing. NULL passed in this parameter indicates DIEs
+ * for all tags should be collected.
+ * next_cu_die - Upon successful return contains pointer to the next
+ * compilation unit descriptor inside mapped .debug_info section of
+ * the ELF file.
+ * Return:
+ * true on success, false on failure.
+ */
+ virtual bool parse(const DwarfParseContext* parse_context,
+ const void** next_cu_die) = 0;
+
+ /* Gets a DIE object referenced by an offset from the beginning of
+ * this CU in the mapped .debug_info section.
+ */
+ virtual DIEObject* get_referenced_die_object(Elf_Word ref) const = 0;
+
+ /* Gets a reference to a DIE object (offset of the DIE from the
+ * beginning of this CU in the mapped .debug_info section.
+ */
+ virtual Elf_Word get_die_reference(const Dwarf_DIE* die) const = 0;
+
+ /* Gets PC address information.
+ * Param:
+ * address - PC address to get information for.
+ * info - Upon success contains information about routine that belongs to
+ * this compilation unit, containing the given address.
+ * Return:
+ * true on success, or false if this CU doesn't contain the given address.
+ */
+ virtual bool get_pc_address_file_info(Elf_Xword address,
+ Dwarf_AddressInfo* info) = 0;
+
+ /* Gets file descriptor in the mapped .debug_line section of ELF file for a
+ * given index in the file descriptor list.
+ * Param:
+ * index - 1-based index of file descriptor in the file descriptor list.
+ * Return:
+ * File descriptor for the given index, or NULL if index was too big.
+ * NOTE: pointer returned from this method addressed mapped section of
+ * ELF file.
+ */
+ virtual const Dwarf_STMTL_FileDesc* get_stmt_file_info(Elf_Word index) = 0;
+
+ /* Gets directory name by an index in the mapped .debug_line section of
+ * ELF file.
+ * Param:
+ * dir_index - Index of the directory in the file descriptor list. If this
+ * parameter is zero, compilation directory (DW_AT_comp_dir) for this CU
+ * will be returned.
+ * Return:
+ * Directory name for the given index, or NULL if index was too big.
+ * NOTE: pointer returned from this method addressed mapped section of
+ * ELF file.
+ */
+ virtual const char* get_stmt_dir_name(Elf_Word dir_index) = 0;
+
+ protected:
+ /* DIE abbreviation descriptors, cached for this compilation unit. */
+ DwarfAbbrDieArray abbrs_;
+
+ /* Instance of an ELF file that contains this compilation unit. */
+ ElfFile* elf_file_;
+
+ /* DIE object for this CU. */
+ DIEObject* cu_die_;
+
+ /* Next compilation unit in the list (previous in the order they've been
+ * discovered during ELF file parsing).
+ */
+ DwarfCU* prev_cu_;
+
+ /* DWARF version for this CU. */
+ Elf_Half version_;
+
+ /* Byte size of the pointer type for this compilation unit. */
+ Elf_Byte addr_sizeof_;
+};
+
+/* Encapsulates architecture-dependent functionality of a compilation unit.
+ * Template param:
+ * Dwarf_CUHdr - type compilation unit header in the mapped .debug_info
+ * section of ELF file. Must be:
+ * - Dwarf32_CUHdr for 32-bit DWARF, or
+ * - Dwarf64_CUHdr for 64-bit DWARF.
+ * Dwarf_Off - type for an offset field in DWARF data format. Must be:
+ * - Dwarf32_Off for 32-bit DWARF, or
+ * - Dwarf64_Off for 64-bit DWARF.
+ */
+template <typename Dwarf_CUHdr, typename Dwarf_Off>
+class DwarfCUImpl : public DwarfCU {
+ public:
+ /* Constructs DwarfCU instance.
+ * Param:
+ * elf - Instance of ElfFile containing this compilation unit.
+ * hdr - Pointer to compilation unit header inside mapped .debug_info
+ * section of the ELF file.
+ */
+ DwarfCUImpl(ElfFile* elf, const Dwarf_CUHdr* hdr);
+
+ /* Destructs DwarfCU instance. */
+ ~DwarfCUImpl() {
+ }
+
+ /* Parses this compilation unit in .debug_info section, collecting children
+ * DIEs of this compilation unit. This is an implementation of DwarfCU's
+ * abstract metod.
+ * See DwarfCU::parse().
+ */
+ bool parse(const DwarfParseContext* parse_context,
+ const void** next_cu_die);
+
+ /* Gets PC address information.
+ * This is an implementation of DwarfCU's abstract metod.
+ * See DwarfCU::get_pc_address_file_info().
+ */
+ bool get_pc_address_file_info(Elf_Xword address, Dwarf_AddressInfo* info);
+
+ /* Gets file descriptor in the mapped .debug_line section of ELF file for a
+ * given index in the file descriptor list.
+ * This is an implementation of DwarfCU's abstract metod.
+ * See DwarfCU::get_stmt_file_info().
+ */
+ const Dwarf_STMTL_FileDesc* get_stmt_file_info(Elf_Word index);
+
+ /* Gets directory name by an index in the mapped .debug_line section of
+ * ELF file.
+ * This is an implementation of DwarfCU's abstract metod.
+ * See DwarfCU::get_stmt_dir_name().
+ */
+ const char* get_stmt_dir_name(Elf_Word dir_index);
+
+ /* Gets a DIE object referenced by an offset from the beginning of
+ * this CU. This is an implementation of DwarfCU's abstract metod.
+ */
+ DIEObject* get_referenced_die_object(Elf_Word ref) const {
+ const Dwarf_DIE* die = get_referenced_die(ref);
+ return cu_die_->find_die_object(die);
+ }
+
+ /* Gets a reference to a DIE object (offset of the DIE from the
+ * beginning of this CU in the mapped .debug_info section.
+ * This is an implementation of DwarfCU's abstract metod.
+ */
+ Elf_Word get_die_reference(const Dwarf_DIE* die) const {
+ return static_cast<Elf_Word>(diff_ptr(cu_header_, die));
+ }
+
+ protected:
+ /* Process a child DIE (and all its children) in this compilation unit.
+ * Param:
+ * parse_context - See DwarfCU::parse().
+ * die - DIE descriptor of the child to process in this method.
+ * parent_obj - Parent object of the child to process in this method.
+ * NOTE: this parameter can be NULL only for a DIE that represents this
+ * compilation unit itself.
+ * Return:
+ * Pointer to the end of child's attribute list in the mapped .debug_info
+ * section on success, or NULL on failure. Usually, pointer returned from
+ * this method is simply discarded, since parent calculates address of the
+ * next sibling's DIE based on DW_AT_sibling attribute of the DIE preceding
+ * child's DIE.
+ */
+ const Elf_Byte* process_DIE(const DwarfParseContext* parse_context,
+ const Dwarf_DIE* die,
+ DIEObject* parent_obj);
+
+ /* Creates a DIE object for the given DIE.
+ * Param:
+ * parse_context See DwarfCU::parse().
+ * die - DIE to create an object for.
+ * parent - Parent DIE object for the one that's being created in this
+ * method.
+ * tag - Tag of the DIE object that's being created in this method.
+ * Return:
+ * Created DIE object. This method may returns NULL in two cases:
+ * - We're not interested in this DIE (decided by looking at 'tag'
+ * parameter. In this case errno should be set to zero.
+ * - Memory allocation has failed. In this case errno should be
+ * set to ENOMEM.
+ */
+ DIEObject* create_die_object(const DwarfParseContext* parse_context,
+ const Dwarf_DIE* die,
+ DIEObject* parent,
+ Dwarf_Tag tag);
+
+ /* Initializes (caches) STMT lines header for this CU. */
+ bool init_stmtl();
+
+ /* Saves current source file information, collected in the state machine by
+ * the "Line Number Program".
+ * Param:
+ * state - State machine collected "Line Number Program" results.
+ * info - Upon success contains source file information, copied over from
+ * the state machine.
+ * Return:
+ * true on success, or false on failure.
+ */
+ bool set_source_info(const DwarfStateMachine* state,
+ Dwarf_AddressInfo* info);
+
+ /* Gets pointer to the DIE descriptor for this CU. */
+ const Dwarf_DIE* get_DIE() const {
+ /* CU's DIE descriptor immediately follows CU header. */
+ return INC_CPTR_T(Dwarf_DIE, cu_header_, sizeof(Dwarf_CUHdr));
+ }
+
+ /* Caches STMTL header from .debug_line section to stmtl_header_.
+ * Template param:
+ * Dwarf_STMTL_Hdr - Dwarf_STMTL_Hdr32, or Dwarf_STMTL_Hdr64, depending
+ * on the header type.
+ * Param:
+ * stmtl_hdr - STMTL header in the mapped .debug_line section to cache.
+ */
+ template <typename Dwarf_STMTL_Hdr>
+ void cache_stmtl(const Dwarf_STMTL_Hdr* stmtl_hdr) {
+ stmtl_header_.unit_length = elf_file()->pull_val(stmtl_hdr->unit_length.size);
+ stmtl_header_.version = elf_file()->pull_val(stmtl_hdr->version);
+ stmtl_header_.header_length = elf_file()->pull_val(stmtl_hdr->header_length);
+ stmtl_header_.min_instruction_len = stmtl_hdr->min_instruction_len;
+ stmtl_header_.default_is_stmt = stmtl_hdr->default_is_stmt;
+ stmtl_header_.line_base = stmtl_hdr->line_base;
+ stmtl_header_.line_range = stmtl_hdr->line_range;
+ stmtl_header_.opcode_base = stmtl_hdr->opcode_base;
+ stmtl_header_.standard_opcode_lengths = &stmtl_hdr->standard_opcode_lengths;
+ stmtl_header_.start = INC_CPTR_T(Elf_Byte, &stmtl_hdr->min_instruction_len,
+ stmtl_header_.header_length);
+ stmtl_header_.end = INC_CPTR_T(Elf_Byte, &stmtl_hdr->version,
+ stmtl_header_.unit_length);
+ stmtl_header_.include_directories =
+ INC_CPTR_T(char, stmtl_header_.standard_opcode_lengths,
+ stmtl_header_.opcode_base - 1);
+ const char* dir = stmtl_header_.include_directories;
+ while (*dir != '\0') {
+ dir += strlen(dir) + 1;
+ stmtl_header_.inc_dir_num++;
+ }
+ stmtl_header_.file_infos = INC_CPTR_T(Dwarf_STMTL_FileDesc, dir, 1);
+ }
+
+ /* Gets a DIE referenced by an offset from the beginning of this CU
+ * in the mapped .debug_info section.
+ */
+ const Dwarf_DIE* get_referenced_die(Elf_Word ref) const {
+ return INC_CPTR_T(Dwarf_DIE, cu_header_, ref);
+ }
+
+ /* Checks if pointer to the DIE attribute is contained within the CU's area
+ * of the mapped .debug_info section.
+ * Param:
+ * ptr - Pointer to the DIE attribute to check.
+ * Return:
+ * true, if pointer to the DIE attribute is contained within the CU's area
+ * of the mapped .debug_info section, or false if attribute pointer goes
+ * beyond CU's area of the mapped .debug_info section.
+ */
+ bool is_attrib_ptr_valid(const void* ptr) const {
+ return diff_ptr(cu_header_, ptr) < cu_size_;
+ }
+
+ protected:
+ /* Pointer to this compilation unit header inside the mapped .debug_info
+ * section of the ELF file.
+ */
+ const Dwarf_CUHdr* cu_header_;
+
+ /* Size of this compilation unit area in the mapped .debug_info section.
+ * This value has been cached off the CU header in order to avoid
+ * endianness conversions.
+ */
+ Dwarf_Off cu_size_;
+
+ /* STMT lines header, cached off mapped .debug_line section. */
+ Dwarf_STMTL_Hdr stmtl_header_;
+};
+
+#endif // ELFF_DWARF_CU_H_
diff --git a/elff/dwarf_defs.h b/elff/dwarf_defs.h new file mode 100644 index 0000000..567df6a --- /dev/null +++ b/elff/dwarf_defs.h @@ -0,0 +1,1000 @@ +/* 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 structures
+ * describing DWARF format.
+ */
+
+#ifndef ELFF_DWARF_DEFS_H_
+#define ELFF_DWARF_DEFS_H_
+
+#include "dwarf.h"
+#include "elf_defs.h"
+
+/* DWARF structures are packed to 1 byte. */
+#define ELFF_PACKED __attribute__ ((packed))
+
+/*
+ * Helper types for misc. DWARF variables.
+ */
+
+/* Type for DWARF abbreviation number. */
+typedef uint32_t Dwarf_AbbrNum;
+
+/* Type for DWARF tag ID. */
+typedef uint16_t Dwarf_Tag;
+
+/* Type for DWARF attribute ID. */
+typedef uint16_t Dwarf_At;
+
+/* Type for DWARF form ID. */
+typedef uint16_t Dwarf_Form;
+
+/* Type for offset in 32-bit DWARF. */
+typedef uint32_t Dwarf32_Off;
+
+/* Type for offset in 64-bit DWARF. */
+typedef uint64_t Dwarf64_Off;
+
+/* Enumerates types of values, obtained during DWARF attribute decoding. */
+typedef enum DwarfValueType {
+ /* Undefined */
+ DWARF_VALUE_UNKNOWN = 1,
+
+ /* uint8_t */
+ DWARF_VALUE_U8,
+
+ /* int8_t */
+ DWARF_VALUE_S8,
+
+ /* uint16_t */
+ DWARF_VALUE_U16,
+
+ /* int16_t */
+ DWARF_VALUE_S16,
+
+ /* uint32_t */
+ DWARF_VALUE_U32,
+
+ /* int32_t */
+ DWARF_VALUE_S32,
+
+ /* uint64_t */
+ DWARF_VALUE_U64,
+
+ /* int64_t */
+ DWARF_VALUE_S64,
+
+ /* const char* */
+ DWARF_VALUE_STR,
+
+ /* 32-bit address */
+ DWARF_VALUE_PTR32,
+
+ /* 64-bit address */
+ DWARF_VALUE_PTR64,
+
+ /* Dwarf_Block */
+ DWARF_VALUE_BLOCK,
+} DwarfValueType;
+
+/* Describes block of data, stored directly in the mapped .debug_info
+ * section. This type is used to represent an attribute encoded with
+ * DW_FORM_block# form.
+ */
+typedef struct Dwarf_Block {
+ /* Pointer to the block data inside mapped .debug_info section. */
+ const void* block_ptr;
+
+ /* Byte size of the block data. */
+ Elf_Word block_size;
+} Dwarf_Block;
+
+/* Describes a value, obtained from the mapped .debug_info section
+ * during DWARF attribute decoding.
+ */
+typedef struct Dwarf_Value {
+ /* Unites all possible data types for the value.
+ * See DwarfValueType for the list of types.
+ */
+ union {
+ Elf_Byte u8;
+ Elf_Sbyte s8;
+ Elf_Half u16;
+ Elf_Shalf s16;
+ Elf_Word u32;
+ Elf_Sword s32;
+ Elf_Xword u64;
+ Elf_Sxword s64;
+ Elf_Word ptr32;
+ Elf_Xword ptr64;
+ const char* str;
+ Dwarf_Block block;
+ };
+
+ /* Value type (defines which variable in the union abowe
+ * contains the value).
+ */
+ DwarfValueType type;
+
+ /* Number of bytes that encode this value in .debug_info section
+ * of ELF file.
+ */
+ Elf_Word encoded_size;
+} Dwarf_Value;
+
+/* DWARF's LEB128 data type. LEB128 is defined as:
+ * Variable Length Data. "Little Endian Base 128" (LEB128) numbers. LEB128 is
+ * a scheme for encoding integers densely that exploits the assumption that
+ * most integers are small in magnitude. (This encoding is equally suitable
+ * whether the target machine architecture represents data in big-endian or
+ * littleendian order. It is "little endian" only in the sense that it avoids
+ * using space to represent the "big" end of an unsigned integer, when the big
+ * end is all zeroes or sign extension bits).
+ *
+ * Unsigned LEB128 numbers are encoded as follows: start at the low order end
+ * of an unsigned integer and chop it into 7-bit chunks. Place each chunk into
+ * the low order 7 bits of a byte. Typically, several of the high order bytes
+ * will be zero; discard them. Emit the remaining bytes in a stream, starting
+ * with the low order byte; set the high order bit on each byte except the last
+ * emitted byte. The high bit of zero on the last byte indicates to the decoder
+ * that it has encountered the last byte. The integer zero is a special case,
+ * consisting of a single zero byte.
+ *
+ * The encoding for signed LEB128 numbers is similar, except that the criterion
+ * for discarding high order bytes is not whether they are zero, but whether
+ * they consist entirely of sign extension bits. Consider the 32-bit integer
+ * -2. The three high level bytes of the number are sign extension, thus LEB128
+ * would represent it as a single byte containing the low order 7 bits, with
+ * the high order bit cleared to indicate the end of the byte stream. Note that
+ * there is nothing within the LEB128 representation that indicates whether an
+ * encoded number is signed or unsigned. The decoder must know what type of
+ * number to expect.
+ *
+ * NOTE: It's assumed that LEB128 will not contain encodings for integers,
+ * larger than 64 bit.
+*/
+typedef struct ELFF_PACKED Dwarf_Leb128 {
+ /* Beginning of the LEB128 block. */
+ Elf_Byte val;
+
+ /* Pulls actual value, encoded with this LEB128 block.
+ * Param:
+ * value - Upon return will contain value, encoded with this LEB128 block.
+ * sign - If true, the caller expects the LEB128 to contain a signed
+ * integer, otherwise, caller expects an unsigned integer value to be
+ * encoded with this LEB128 block.
+ */
+ void get_common(Dwarf_Value* value, bool sign) const {
+ value->u64 = 0;
+ /* Integer zero is a special case. */
+ if (val == 0) {
+ value->type = sign ? DWARF_VALUE_S32 : DWARF_VALUE_U32;
+ value->encoded_size = 1;
+ return;
+ }
+
+ /* We've got to reconstruct the integer. */
+ value->type = DWARF_VALUE_UNKNOWN;
+ value->encoded_size = 0;
+
+ /* Byte by byte loop though the LEB128, reconstructing the integer from
+ * 7-bits chunks. Byte with 8-th bit set to zero indicates the end
+ * of the LEB128 block. For signed integers, 7-th bit of the last LEB128
+ * byte controls the sign. If 7-th bit of the last LEB128 byte is set,
+ * the integer is negative. If 7-th bit of the last LEB128 byte is not
+ * set, the integer is positive.
+ */
+ const Elf_Byte* cur = &val;
+ Elf_Word shift = 0;
+ while ((*cur & 0x80) != 0) {
+ value->u64 |= (static_cast<Elf_Xword>(*cur) & 0x7F) << shift;
+ shift += 7;
+ value->encoded_size++;
+ cur++;
+ }
+ value->u64 |= (static_cast<Elf_Xword>(*cur) & 0x7F) << shift;
+ value->encoded_size++;
+
+ /* LEB128 format doesn't carry any info of the sizeof of the integer it
+ * represents. We well guess it, judging by the highest bit set in the
+ * reconstucted integer.
+ */
+ if ((value->u64 & 0xFFFFFFFF00000000LL) == 0) {
+ /* 32-bit integer. */
+ if (sign) {
+ value->type = DWARF_VALUE_S32;
+ if (((*cur) & 0x40) != 0) {
+ // Value is negative.
+ value->u64 |= - (1 << (shift + 7));
+ } else if ((value->u32 & 0x80000000) != 0) {
+ // Make sure we don't report negative value in this case.
+ value->type = DWARF_VALUE_S64;
+ }
+ } else {
+ value->type = DWARF_VALUE_U32;
+ }
+ } else {
+ /* 64-bit integer. */
+ if (sign) {
+ value->type = DWARF_VALUE_S64;
+ if (((*cur) & 0x40) != 0) {
+ // Value is negative.
+ value->u64 |= - (1 << (shift + 7));
+ }
+ } else {
+ value->type = DWARF_VALUE_U64;
+ }
+ }
+ }
+
+ /* Pulls actual unsigned value, encoded with this LEB128 block.
+ * See get_common() for more info.
+ * Param:
+ * value - Upon return will contain unsigned value, encoded with
+ * this LEB128 block.
+ */
+ void get_unsigned(Dwarf_Value* value) const {
+ get_common(value, false);
+ }
+
+ /* Pulls actual signed value, encoded with this LEB128 block.
+ * See get_common() for more info.
+ * Param:
+ * value - Upon return will contain signed value, encoded with
+ * this LEB128 block.
+ */
+ void get_signed(Dwarf_Value* value) const {
+ get_common(value, true);
+ }
+
+ /* Pulls LEB128 value, advancing past this LEB128 block.
+ * See get_common() for more info.
+ * Return:
+ * Pointer to the byte past this LEB128 block.
+ */
+ const void* process(Dwarf_Value* value, bool sign) const {
+ get_common(value, sign);
+ return INC_CPTR(&val, value->encoded_size);
+ }
+
+ /* Pulls LEB128 unsigned value, advancing past this LEB128 block.
+ * See process() for more info.
+ */
+ const void* process_unsigned(Dwarf_Value* value) const {
+ return process(value, false);
+ }
+
+ /* Pulls LEB128 signed value, advancing past this LEB128 block.
+ * See process() for more info.
+ */
+ const void* process_signed(Dwarf_Value* value) const {
+ return process(value, true);
+ }
+} Dwarf_Leb128;
+
+/* DIE attribute descriptor in the .debug_abbrev section.
+ * Attribute descriptor contains two LEB128 values. First one provides
+ * attribute ID (one of DW_AT_XXX values), and the second one provides
+ * format (one of DW_FORMAT_XXX values), in which attribute value is
+ * encoded in the .debug_info section of the ELF file.
+ */
+typedef struct ELFF_PACKED Dwarf_Abbr_AT {
+ /* Attribute ID (DW_AT_XXX).
+ * Attribute format (DW_FORMAT_XXX) follows immediately.
+ */
+ Dwarf_Leb128 at;
+
+ /* Checks if this is a separator descriptor.
+ * Zero is an invalid attribute ID, indicating the end of attribute
+ * list for the current DIE.
+ */
+ bool is_separator() const {
+ return at.val == 0;
+ }
+
+ /* Pulls attribute data, advancing past this descriptor.
+ * Param:
+ * at_value - Upon return contains attribute value of this descriptor.
+ * form - Upon return contains form value of this descriptor.
+ * Return:
+ * Pointer to the byte past this descriptor block (usually, next
+ * attribute decriptor).
+ */
+ const Dwarf_Abbr_AT* process(Dwarf_At* at_value, Dwarf_Form* form) const {
+ if (is_separator()) {
+ /* Size of separator descriptor is always 2 bytes. */
+ *at_value = 0;
+ *form = 0;
+ return INC_CPTR_T(Dwarf_Abbr_AT, &at.val, 2);
+ }
+
+ Dwarf_Value val;
+
+ /* Process attribute ID. */
+ const Dwarf_Leb128* next =
+ reinterpret_cast<const Dwarf_Leb128*>(at.process_unsigned(&val));
+ *at_value = val.u16;
+
+ /* Follow with processing the form. */
+ next = reinterpret_cast<const Dwarf_Leb128*>(next->process_unsigned(&val));
+ *form = val.u16;
+ return reinterpret_cast<const Dwarf_Abbr_AT*>(next);
+ }
+} Dwarf_Abbr_AT;
+
+/* DIE abbreviation descriptor in the .debug_abbrev section.
+ * DIE abbreviation descriptor contains three parameters. The first one is a
+ * LEB128 value, that encodes 1 - based abbreviation descriptor number.
+ * Abbreviation descriptor numbers seems to be always in sequential order, and
+ * are counted on per-compilation unit basis. I.e. abbreviation number for the
+ * first DIE abbreviation descriptor of each compilation unit is always 1.
+ *
+ * Besides abbreviation number, DIE abbreviation descriptor contains two more
+ * values. The first one (after abbr_num) is a LEB128 value containing DIE's
+ * tag value, and the second one is one byte flag specifying whether or not
+ * the DIE contains any cildren.
+ *
+ * This descriptor is immediately followed by a list of attribute descriptors
+ * (see Dwarf_Abbr_AT) for the DIE represented by this abbreviation descriptor.
+ */
+typedef struct ELFF_PACKED Dwarf_Abbr_DIE {
+ /* 1 - based abbreviation number for the DIE. */
+ Dwarf_Leb128 abbr_num;
+
+ /* Gets abbreviation number for this descriptor. */
+ Dwarf_AbbrNum get_abbr_num() const {
+ Dwarf_Value val;
+ abbr_num.get_unsigned(&val);
+ return val.u16;
+ }
+
+ /* Gets DIE tag for this descriptor. */
+ Dwarf_Tag get_tag() const {
+ Dwarf_Tag tag;
+ process(NULL, &tag);
+ return tag;
+ }
+
+ /* Pulls DIE abbreviation descriptor data, advancing past this descriptor.
+ * Param:
+ * abbr_index - Upon return contains abbreviation number for this
+ * descriptor. This parameter can be NULL, if the caller is not interested
+ * in this value.
+ * tag - Upon return contains tag of the DIE for this descriptor. This
+ * parameter can be NULL, if the caller is not interested in this value.
+ * form - Upon return contains form of the DIE for this descriptor.
+ * Return:
+ * Pointer to the list of attribute descriptors for the DIE.
+ */
+ const Dwarf_Abbr_AT* process(Dwarf_AbbrNum* abbr_index,
+ Dwarf_Tag* tag) const {
+ Dwarf_Value val;
+ const Dwarf_Leb128* next =
+ reinterpret_cast<const Dwarf_Leb128*>(abbr_num.process_unsigned(&val));
+ if (abbr_index != NULL) {
+ *abbr_index = val.u32;
+ }
+
+ /* Next one is a "tag". */
+ next = reinterpret_cast<const Dwarf_Leb128*>(next->process_unsigned(&val));
+ if (tag != NULL) {
+ *tag = val.u16;
+ }
+
+ /* Next one is a "has children" one byte flag. We're not interested in it,
+ * so jump to the list of attribute descriptors that immediately follows
+ * this DIE descriptor. */
+ return INC_CPTR_T(Dwarf_Abbr_AT, next, 1);
+ }
+} Dwarf_Abbr_DIE;
+
+/* DIE descriptor in the .debug_info section.
+ * DIE descriptor contains one LEB128-encoded value, containing DIE's
+ * abbreviation descriptor number in the .debug_abbrev section.
+ *
+ * DIE descriptor is immediately followed by the list of DIE attribute values,
+ * format of wich is defined by the list of attribute descriptors in the
+ * .debug_abbrev section, that immediately follow the DIE attribute descriptor,
+ * addressed by this descriptor's abbr_num LEB128.
+ */
+typedef struct ELFF_PACKED Dwarf_DIE {
+ /* 1 - based index of DIE abbreviation descriptor (Dwarf_Abbr_DIE) for this
+ * DIE in the .debug_abbrev section.
+ *
+ * NOTE: DIE abbreviation descriptor indexes are tied to the compilation
+ * unit. In other words, each compilation unit restarts counting DIE
+ * abbreviation descriptors from 1.
+ *
+ * NOTE: Zero is invalid value for this field, indicating that this DIE is a
+ * separator (usually it ends a list of "child" DIEs)
+ */
+ Dwarf_Leb128 abbr_num;
+
+ /* Checks if this is a separator DIE. */
+ bool is_separator() const {
+ return abbr_num.val == 0;
+ }
+
+ /* Gets (1 - based) abbreviation number for this DIE. */
+ Dwarf_AbbrNum get_abbr_num() const {
+ Dwarf_Value val;
+ abbr_num.get_unsigned(&val);
+ return val.u16;
+ }
+
+ /* Pulls DIE information, advancing past this descriptor to DIE attributes.
+ * Param:
+ * abbr_num - Upon return contains abbreviation number for this DIE. This
+ * parameter can be NULL, if the caller is not interested in this value.
+ * Return:
+ * Pointer to the byte past this descriptor (the list of DIE attributes).
+ */
+ const Elf_Byte* process(Dwarf_AbbrNum* abbr_number) const {
+ if (is_separator()) {
+ if (abbr_number != NULL) {
+ *abbr_number = 0;
+ }
+ // Size of a separator DIE is 1 byte.
+ return INC_CPTR_T(Elf_Byte, &abbr_num.val, 1);
+ }
+ Dwarf_Value val;
+ const void* ret = abbr_num.process_unsigned(&val);
+ if (abbr_number != NULL) {
+ *abbr_number = val.u32;
+ }
+ return reinterpret_cast<const Elf_Byte*>(ret);
+ }
+} Dwarf_DIE;
+
+/*
+ * Variable size headers.
+ * When encoding size value in DWARF, the first 32 bits of a "size" header
+ * define header type. If first 32 bits of the header contain 0xFFFFFFFF
+ * value, this is 64-bit size header with the following 64 bits encoding
+ * the size. Otherwise, if first 32 bits are not 0xFFFFFFFF, they contain
+ * 32-bit size value.
+ */
+
+/* Size header for 32-bit DWARF. */
+typedef struct ELFF_PACKED Dwarf32_SizeHdr {
+ /* Size value. */
+ Elf_Word size;
+} Dwarf32_SizeHdr;
+
+/* Size header for 64-bit DWARF. */
+typedef struct ELFF_PACKED Dwarf64_SizeHdr {
+ /* Size selector. For 64-bit DWARF this field is set to 0xFFFFFFFF */
+ Elf_Word size_selector;
+
+ /* Actual size value. */
+ Elf_Xword size;
+} Dwarf64_SizeHdr;
+
+/* Compilation unit header in the .debug_info section.
+ * Template param:
+ * Dwarf_SizeHdr - Type for the header's size field. Must be Dwarf32_SizeHdr
+ * for 32-bit DWARF, or Dwarf64_SizeHdr for 64-bit DWARF.
+ * Elf_Off - Type for abbrev_offset field. Must be Elf_Word for for 32-bit
+ * DWARF, or Elf_Xword for 64-bit DWARF.
+ */
+template <typename Dwarf_SizeHdr, typename Elf_Off>
+struct ELFF_PACKED Dwarf_CUHdr {
+ /* Size of the compilation unit data in .debug_info section. */
+ Dwarf_SizeHdr size_hdr;
+
+ /* Compilation unit's DWARF version stamp. */
+ Elf_Half version;
+
+ /* Relative (to the beginning of .debug_abbrev section data) offset of the
+ * beginning of abbreviation sequence for this compilation unit.
+ */
+ Elf_Off abbrev_offset;
+
+ /* Pointer size for this compilation unit (should be 4, or 8). */
+ Elf_Byte address_size;
+};
+/* Compilation unit header in the .debug_info section for 32-bit DWARF. */
+typedef Dwarf_CUHdr<Dwarf32_SizeHdr, Elf_Word> Dwarf32_CUHdr;
+/* Compilation unit header in the .debug_info section for 64-bit DWARF. */
+typedef Dwarf_CUHdr<Dwarf64_SizeHdr, Elf_Xword> Dwarf64_CUHdr;
+
+/* CU STMTL header in the .debug_line section.
+ * Template param:
+ * Dwarf_SizeHdr - Type for the header's size field. Must be Dwarf32_SizeHdr
+ * for 32-bit DWARF, or Dwarf64_SizeHdr for 64-bit DWARF.
+ * Elf_Size - Type for header_length field. Must be Elf_Word for for 32-bit
+ * DWARF, or Elf_Xword for 64-bit DWARF.
+ */
+template <typename Dwarf_SizeHdr, typename Elf_Size>
+struct ELFF_PACKED Dwarf_STMTLHdr {
+ /* The size in bytes of the line number information for this compilation
+ * unit, not including the unit_length field itself. */
+ Dwarf_SizeHdr unit_length;
+
+ /* A version number. This number is specific to the line number information
+ * and is independent of the DWARF version number. */
+ Elf_Half version;
+
+ /* The number of bytes following the header_length field to the beginning of
+ * the first byte of the line number program itself. In the 32-bit DWARF
+ * format, this is a 4-byte unsigned length; in the 64-bit DWARF format,
+ * this field is an 8-byte unsigned length. */
+ Elf_Size header_length;
+
+ /* The size in bytes of the smallest target machine instruction. Line number
+ * program opcodes that alter the address register first multiply their
+ * operands by this value. */
+ Elf_Byte min_instruction_len;
+
+ /* The initial value of the is_stmt register. */
+ Elf_Byte default_is_stmt;
+
+ /* This parameter affects the meaning of the special opcodes. */
+ Elf_Sbyte line_base;
+
+ /* This parameter affects the meaning of the special opcodes. */
+ Elf_Byte line_range;
+
+ /* The number assigned to the first special opcode. */
+ Elf_Byte opcode_base;
+
+ /* This is first opcode in an array specifying the number of LEB128 operands
+ * for each of the standard opcodes. The first element of the array
+ * corresponds to the opcode whose value is 1, and the last element
+ * corresponds to the opcode whose value is opcode_base - 1. By increasing
+ * opcode_base, and adding elements to this array, new standard opcodes can
+ * be added, while allowing consumers who do not know about these new opcodes
+ * to be able to skip them. NOTE: this array points to the mapped
+ * .debug_line section. */
+ Elf_Byte standard_opcode_lengths;
+};
+/* CU STMTL header in the .debug_line section for 32-bit DWARF. */
+typedef Dwarf_STMTLHdr<Dwarf32_SizeHdr, Elf_Word> Dwarf32_STMTLHdr;
+/* CU STMTL header in the .debug_line section for 64-bit DWARF. */
+typedef Dwarf_STMTLHdr<Dwarf64_SizeHdr, Elf_Xword> Dwarf64_STMTLHdr;
+
+/* Source file descriptor in the .debug_line section.
+ * Descriptor begins with zero-terminated file name, followed by an ULEB128,
+ * encoding directory index in the list of included directories, followed by
+ * an ULEB12, encoding file modification time, followed by an ULEB12, encoding
+ * file size.
+ */
+typedef struct ELFF_PACKED Dwarf_STMTL_FileDesc {
+ /* Zero-terminated file name. */
+ char file_name[1];
+
+ /* Checks of this descriptor ends the list. */
+ bool is_last_entry() const {
+ return file_name[0] == '\0';
+ }
+
+ /* Gets file name. */
+ const char* get_file_name() const {
+ return file_name;
+ }
+
+ /* Processes this descriptor, advancing to the next one.
+ * Param:
+ * dir_index - Upon return contains index of the parent directory in the
+ * list of included directories. Can be NULL if caller is not interested
+ * in this value.
+ * Return:
+ * Pointer to the next source file descriptor in the list.
+ */
+ const Dwarf_STMTL_FileDesc* process(Elf_Word* dir_index) const {
+ if (is_last_entry()) {
+ return this;
+ }
+
+ /* First parameter: include directory index. */
+ Dwarf_Value tmp;
+ const Dwarf_Leb128* leb =
+ INC_CPTR_T(Dwarf_Leb128, file_name, strlen(file_name) + 1);
+ leb = reinterpret_cast<const Dwarf_Leb128*>(leb->process_unsigned(&tmp));
+ if (dir_index != NULL) {
+ *dir_index = tmp.u32;
+ }
+ /* Process file time. */
+ leb = reinterpret_cast<const Dwarf_Leb128*>(leb->process_unsigned(&tmp));
+ /* Process file size. */
+ return reinterpret_cast<const Dwarf_STMTL_FileDesc*>(leb->process_unsigned(&tmp));
+ }
+
+ /* Gets directory index for this descriptor. */
+ Elf_Word get_dir_index() const {
+ assert(!is_last_entry());
+ if (is_last_entry()) {
+ return 0;
+ }
+ /* Get directory index. */
+ Dwarf_Value ret;
+ const Dwarf_Leb128* leb =
+ INC_CPTR_T(Dwarf_Leb128, file_name, strlen(file_name) + 1);
+ leb->process_unsigned(&ret);
+ return ret.u32;
+ }
+} Dwarf_STMTL_FileDesc;
+
+/* Encapsulates a DIE attribute, collected during ELF file parsing.
+ */
+class DIEAttrib {
+ public:
+ /* Constructs DIEAttrib intance. */
+ DIEAttrib()
+ : at_(0),
+ form_(0) {
+ value_.type = DWARF_VALUE_UNKNOWN;
+ }
+
+ /* Destructs DIEAttrib intance. */
+ ~DIEAttrib() {
+ }
+
+ /* Gets DWARF attribute ID (DW_AT_Xxx) for this property. */
+ Dwarf_At at() const {
+ return at_;
+ }
+
+ /* Gets DWARF form ID (DW_FORM_Xxx) for this property. */
+ Dwarf_Form form() const {
+ return form_;
+ }
+
+ /* Gets value of this property. */
+ const Dwarf_Value* value() const {
+ return &value_;
+ }
+
+ /* Value of this property. */
+ Dwarf_Value value_;
+
+ /* DWARF attribute ID (DW_AT_Xxx) for this property. */
+ Dwarf_At at_;
+
+ /* DWARF form ID (DW_FORM_Xxx) for this property. */
+ Dwarf_Form form_;
+};
+
+/* Parse tag context.
+ * This structure is used as an ELF file parsing parameter, limiting collected
+ * DIEs by the list of tags.
+ */
+typedef struct DwarfParseContext {
+ /* Zero-terminated list of tags to collect DIEs for. If this field is NULL,
+ * DIEs for all tags will be collected during the parsing. */
+ const Dwarf_Tag* tags;
+} DwarfParseContext;
+
+/* Checks if a DIE with the given tag should be collected during the parsing.
+ * Param:
+ * parse_context - Parse context to check the tag against. This parameter can
+ * be NULL, indicating that all tags should be collected.
+ * tag - Tag to check.
+ * Return:
+ * true if a DIE with the given tag should be collected during the parsing,
+ * or false, if the DIE should not be collected.
+ */
+static inline bool
+collect_die(const DwarfParseContext* parse_context, Dwarf_Tag tag) {
+ if (parse_context == NULL || parse_context->tags == NULL) {
+ return true;
+ }
+ for (const Dwarf_Tag* tags = parse_context->tags; *tags != 0; tags++) {
+ if (*tags == tag) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Encapsulates an array of Dwarf_Abbr_DIE pointers, cached for a compilation
+ * unit. Although Dwarf_Abbr_DIE descriptors in the .debug_abbrev section of
+ * the ELF file seems to be always in sequential order, DIE descriptors may
+ * reference them randomly. So, to provide better performance, we will cache
+ * all Dwarf_Abbr_DIE pointers, that were found for each DIE. Since all of the
+ * Dwarf_Abbr_DIE are sequential, an array is the best way to cache them.
+ *
+ * NOTE: Objects of this class are instantiated one per each CU, as all DIE
+ * abbreviation numberation is restarted from 1 for each new CU.
+ */
+class DwarfAbbrDieArray {
+ public:
+ /* Constructs DwarfAbbrDieArray instance.
+ * Most of the CUs don't have too many unique Dwarf_Abbr_DIEs, so, in order
+ * to decrease the amount of memory allocation calls, we will preallocate
+ * a relatively small array for them along with the instance of this class,
+ * hopping, that all Dwarf_Abbr_DIEs for the CU will fit into it.
+ */
+ DwarfAbbrDieArray()
+ : count_(0),
+ array_size_(ELFF_ARRAY_SIZE(small_array_)),
+ array_(&small_array_[0]) {
+ }
+
+ /* Destructs DwarfAbbrDieArray instance. */
+ ~DwarfAbbrDieArray() {
+ if (array_ != &small_array_[0]) {
+ delete[] array_;
+ }
+ }
+
+ /* Adds new entry to the array
+ * Param:
+ * abbr - New entry to add.
+ * num - Abbreviation number for the adding entry.
+ * NOTE: before adding, this method will verify that descriptor for the
+ * given abbreviation number has not been cached yet.
+ * NOTE: due to the nature of this array, entries MUST be added strictly
+ * in sequential order.
+ * Return:
+ * true on success, false on failure.
+ */
+ bool add(const Dwarf_Abbr_DIE* abbr, Dwarf_AbbrNum num) {
+ assert(num != 0);
+ if (num == 0) {
+ // Zero is illegal DIE abbreviation number.
+ _set_errno(EINVAL);
+ return false;
+ }
+
+ if (num <= count_) {
+ // Already cached.
+ return true;
+ }
+
+ // Enforce strict sequential order.
+ assert(num == (count_ + 1));
+ if (num != (count_ + 1)) {
+ _set_errno(EINVAL);
+ return false;
+ }
+
+ if (num >= array_size_) {
+ /* Expand the array. Make it 64 entries bigger than adding entry number.
+ * NOTE: that we don't check for an overflow here, since we secured
+ * ourselves from that by enforcing strict sequential order. So, an
+ * overflow may happen iff number of entries cached in this array is
+ * close to 4G, which is a) totally unreasonable, and b) we would die
+ * long before this amount of entries is cached.
+ */
+ Dwarf_AbbrNum new_size = num + 64;
+
+ // Reallocate.
+ const Dwarf_Abbr_DIE** new_array = new const Dwarf_Abbr_DIE*[new_size];
+ assert(new_array != NULL);
+ if (new_array == NULL) {
+ _set_errno(ENOMEM);
+ return false;
+ }
+ memcpy(new_array, array_, count_ * sizeof(const Dwarf_Abbr_DIE*));
+ if (array_ != &small_array_[0]) {
+ delete[] array_;
+ }
+ array_ = new_array;
+ array_size_ = new_size;
+ }
+
+ // Abbreviation numbers are 1-based.
+ array_[num - 1] = abbr;
+ count_++;
+ return true;
+ }
+
+ /* Adds new entry to the array
+ * Param:
+ * abbr - New entry to add.
+ * Return:
+ * true on success, false on failure.
+ */
+ bool add(const Dwarf_Abbr_DIE* abbr) {
+ return add(abbr, abbr->get_abbr_num());
+ }
+
+ /* Gets an entry from the array
+ * Param:
+ * num - 1-based index of an entry to get.
+ * Return:
+ * Entry on success, or NULL if num exceeds the number of entries
+ * contained in the array.
+ */
+ const Dwarf_Abbr_DIE* get(Dwarf_AbbrNum num) const {
+ assert(num != 0 && num <= count_);
+ if (num != 0 && num <= count_) {
+ return array_[num - 1];
+ } else {
+ _set_errno(EINVAL);
+ return NULL;
+ }
+ }
+
+ /* Caches Dwarf_Abbr_DIEs into this array up to the requested number.
+ * NOTE: This method cannot be called on an empty array. Usually, first
+ * entry is inserted into this array when CU object is initialized.
+ * Param:
+ * num - Entry number to cache entries up to.
+ * Return:
+ * Last cached entry (actually, an entry for the 'num' index).
+ */
+ const Dwarf_Abbr_DIE* cache_to(Dwarf_AbbrNum num) {
+ /* Last cached DIE abbreviation. We always should have cached at least one
+ * abbreviation for the CU DIE itself, added via "add" method when CU
+ * object was initialized. */
+ const Dwarf_Abbr_DIE* cur_abbr = get(count_);
+ assert(cur_abbr != NULL);
+ if (cur_abbr == NULL) {
+ return NULL;
+ }
+
+ /* Starting with the last cached DIE abbreviation, loop through the
+ * remaining DIE abbreviations in the .debug_abbrev section of the
+ * mapped ELF file, caching them until we reach the requested
+ * abbreviation descriptor number. Normally, the very next DIE
+ * abbreviation will stop the loop. */
+ while (num > count_) {
+ Dwarf_AbbrNum abbr_num;
+ Dwarf_Tag tmp2;
+ Dwarf_Form tmp3;
+ Dwarf_At tmp4;
+
+ /* Process all AT abbreviations for the current DIE entry, reaching next
+ * DIE abbreviation. */
+ const Dwarf_Abbr_AT* abbr_at = cur_abbr->process(&abbr_num, &tmp2);
+ while (!abbr_at->is_separator()) {
+ abbr_at = abbr_at->process(&tmp4, &tmp3);
+ }
+
+ // Next DIE abbreviation is right after the separator AT abbreviation.
+ cur_abbr = reinterpret_cast<const Dwarf_Abbr_DIE*>
+ (abbr_at->process(&tmp4, &tmp3));
+ if (!add(cur_abbr)) {
+ return NULL;
+ }
+ }
+
+ return array_[num - 1];
+ }
+
+ /* Empties array and frees allocations. */
+ void empty() {
+ if (array_ != &small_array_[0]) {
+ delete[] array_;
+ array_ = &small_array_[0];
+ array_size_ = sizeof(small_array_) / sizeof(small_array_[0]);
+ }
+ count_ = 0;
+ }
+
+ protected:
+ /* Array, preallocated in anticipation of relatively small number of
+ * DIE abbreviations in compilation unit. */
+ const Dwarf_Abbr_DIE* small_array_[64];
+
+ /* Array of Dwarf_Abbr_DIE pointers, cached for a compilation unit. */
+ const Dwarf_Abbr_DIE** array_;
+
+ /* Current size of the array. */
+ Dwarf_AbbrNum array_size_;
+
+ /* Number of entries, cached in the array. */
+ Dwarf_AbbrNum count_;
+};
+
+/* Encapsulates a state machine for the "Line Number Program", that is run
+ * on data conained in the mapped .debug_line section.
+ */
+class DwarfStateMachine {
+ public:
+ /* Constructs DwarfStateMachine instance.
+ * Param:
+ * set_is_stmt - Matches value of default_is_stmt field in the STMTL header.
+ * see Dwarf_STMTL_HdrXX.
+ */
+ explicit DwarfStateMachine(bool set_is_stmt)
+ : address_(0),
+ file_(1),
+ line_(1),
+ column_(0),
+ discriminator_(0),
+ is_stmt_(set_is_stmt),
+ basic_block_(false),
+ end_sequence_(false),
+ prologue_end_(false),
+ epilogue_begin_(false),
+ isa_(0),
+ set_file_info_(NULL) {
+ }
+
+ /* Destructs DwarfStateMachine instance. */
+ ~DwarfStateMachine() {
+ }
+
+ /* Resets the state to default.
+ * Param:
+ * set_is_stmt - Matches value of default_is_stmt field in the STMTL header.
+ * see Dwarf_STMTL_HdrXX.
+ */
+ void reset(bool set_is_stmt) {
+ address_ = 0;
+ file_ = 1;
+ line_ = 1;
+ column_ = 0;
+ discriminator_ = 0;
+ is_stmt_ = set_is_stmt;
+ basic_block_ = false;
+ end_sequence_ = false;
+ prologue_end_ = false;
+ epilogue_begin_ = false;
+ isa_ = 0;
+ set_file_info_ = NULL;
+ }
+
+ /*
+ * Machine state.
+ */
+
+ /* Current address (current PC value). */
+ Elf_Xword address_;
+
+ /* Current index of source file descriptor. */
+ Elf_Word file_;
+
+ /* Current line in the current source file. */
+ Elf_Word line_;
+
+ /* Current column. */
+ Elf_Word column_;
+
+ /* Current discriminator value. */
+ Elf_Word discriminator_;
+
+ /* Current STMT flag. */
+ bool is_stmt_;
+
+ /* Current basic block flag. */
+ bool basic_block_;
+
+ /* Current end of sequence flag. */
+ bool end_sequence_;
+
+ /* Current end of prologue flag. */
+ bool prologue_end_;
+
+ /* Current epilogue begin flag. */
+ bool epilogue_begin_;
+
+ /* Current ISA value. */
+ Elf_Word isa_;
+
+ /* Current value for explicitly set current source file descriptor.
+ * If not NULL, this descriptor has priority over the descriptor, addressed
+ * by the file_ member of this class. */
+ const Dwarf_STMTL_FileDesc* set_file_info_;
+};
+
+/* Checks if given tag belongs to a routine. */
+static inline bool
+dwarf_tag_is_routine(Dwarf_Tag tag) {
+ return tag == DW_TAG_inlined_subroutine ||
+ tag == DW_TAG_subprogram ||
+ tag == DW_AT_main_subprogram;
+}
+
+/* Checks if given tag belongs to a compilation unit. */
+static inline bool
+dwarf_tag_is_cu(Dwarf_Tag tag) {
+ return tag == DW_TAG_compile_unit ||
+ tag == DW_TAG_partial_unit;
+}
+
+#endif // ELFF_DWARF_DEFS_H_
diff --git a/elff/dwarf_die.cc b/elff/dwarf_die.cc new file mode 100644 index 0000000..2085e31 --- /dev/null +++ b/elff/dwarf_die.cc @@ -0,0 +1,269 @@ +/* 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 implementations of classes defined for a variety of DWARF objects.
+ */
+
+#include "stdio.h"
+#include "dwarf_die.h"
+#include "dwarf_cu.h"
+#include "dwarf_utils.h"
+#include "elf_file.h"
+
+DIEObject::~DIEObject() {
+ /* Delete all children of this object. */
+ DIEObject* to_del = last_child();
+ while (to_del != NULL) {
+ DIEObject* next = to_del->prev_sibling();
+ delete to_del;
+ to_del = next;
+ }
+}
+
+ElfFile* DIEObject::elf_file() const {
+ return parent_cu()->elf_file();
+}
+
+Dwarf_Tag DIEObject::get_tag() const {
+ Dwarf_Tag tag;
+ return advance(NULL, &tag) != NULL ? tag : 0;
+}
+
+const char* DIEObject::get_name() const {
+ DIEAttrib die_attr;
+ /* Start with the obvious. */
+ if (get_attrib(DW_AT_name, &die_attr)) {
+ return die_attr.value()->str;
+ }
+
+ /* Lets see if there is a reference to the abstract origin, or specification,
+ * and use its name as the name for this DIE. */
+ if (get_attrib(DW_AT_abstract_origin, &die_attr) ||
+ get_attrib(DW_AT_specification, &die_attr)) {
+ DIEObject* org_die_obj =
+ parent_cu()->get_referenced_die_object(die_attr.value()->u32);
+ if (org_die_obj != NULL) {
+ return org_die_obj->get_name();
+ }
+ }
+
+ /* Lets see if there is a reference to the type DIE, and use
+ * its name as the name for this DIE. */
+ if (get_attrib(DW_AT_type, &die_attr)) {
+ DIEObject* org_die_obj =
+ parent_cu()->get_referenced_die_object(die_attr.value()->u32);
+ if (org_die_obj != NULL) {
+ return org_die_obj->get_name();
+ }
+ }
+
+ /* Can't figure the name for this DIE. */
+ return NULL;
+}
+
+bool DIEObject::get_attrib(Dwarf_At at_id, DIEAttrib* attr) const {
+ const Dwarf_Abbr_AT* at_abbr;
+
+ /* Advance to DIE attributes. */
+ const Elf_Byte* die_attr = advance(&at_abbr, NULL);
+ if (die_attr == NULL) {
+ _set_errno(EINVAL);
+ return false;
+ }
+
+ /* Loop through all DIE attributes, looking for the one that's being
+ * requested. */
+ while (!at_abbr->is_separator()) {
+ at_abbr = at_abbr->process(&attr->at_, &attr->form_);
+ die_attr = parent_cu()->process_attrib(die_attr, attr->form_, &attr->value_);
+ if (at_id == attr->at()) {
+ return true;
+ }
+ }
+
+ _set_errno(EINVAL);
+
+ return false;
+}
+
+DIEObject* DIEObject::get_leaf_for_address(Elf_Xword address) {
+ const bool contains = parent_cu()->is_CU_address_64() ?
+ contains_address<Elf_Xword>(address) :
+ contains_address<Elf_Word>(address);
+ if (!contains && !is_cu_die()) {
+ /* For CU DIEs address range may be zero size, even though its child DIEs
+ * occupie some address space. So, if CU DIE's address range doesn't
+ * contain the given address, we still want to go and check the children.
+ */
+ _set_errno(EINVAL);
+ return NULL;
+ }
+
+ /* This DIE contains given address (or may contain it, if this is a CU DIE).
+ * Lets iterate through child DIEs to find the leaf (last DIE) that contains
+ * this address. */
+ DIEObject* child = last_child();
+ while (child != NULL) {
+ DIEObject* leaf = child->get_leaf_for_address(address);
+ if (leaf != NULL) {
+ return leaf;
+ }
+ child = child->prev_sibling();
+ }
+ /* No child DIE contains this address. This DIE is the leaf. */
+ return contains || !is_cu_die() ? this : NULL;
+}
+
+template <typename AddrType>
+bool DIEObject::contains_address(Elf_Xword address) {
+ DIEAttrib die_ranges;
+ /* DIE can contain either list of ranges (f.i. DIEs that represent a routine
+ * that is inlined in multiple places will contain list of address ranges
+ * where that routine is inlined), or a pair "low PC, and high PC" describing
+ * contiguos address space where routine has been placed by compiler. */
+ if (get_attrib(DW_AT_ranges, &die_ranges)) {
+ /* Iterate through this DIE's ranges list, looking for the one that
+ * contains the given address. */
+ AddrType low;
+ AddrType high;
+ Elf_Word range_off = die_ranges.value()->u32;
+ while (elf_file()->get_range(range_off, &low, &high) &&
+ (low != 0 || high != 0)) {
+ if (address >= low && address < high) {
+ return true;
+ }
+ range_off += sizeof(AddrType) * 2;
+ }
+ return false;
+ } else {
+ /* This DIE doesn't have ranges. Lets see if it has low_pc and high_pc
+ * attributes. */
+ DIEAttrib low_pc;
+ DIEAttrib high_pc;
+ if (!get_attrib(DW_AT_low_pc, &low_pc) ||
+ !get_attrib(DW_AT_high_pc, &high_pc) ||
+ address < low_pc.value()->u64 ||
+ address >= high_pc.value()->u64) {
+ return false;
+ }
+ return true;
+ }
+}
+
+DIEObject* DIEObject::find_die_object(const Dwarf_DIE* die_to_find) {
+ if (die_to_find == die()) {
+ return this;
+ }
+
+ /* First we will iterate through the list of children, since chances to
+ * find requested DIE decrease as we go deeper into DIE tree. */
+ DIEObject* iter = last_child();
+ while (iter != NULL) {
+ if (iter->die() == die_to_find) {
+ return iter;
+ }
+ iter = iter->prev_sibling();
+ };
+
+ /* DIE has not been found among the children. Lets go deeper now. */
+ iter = last_child();
+ while (iter != NULL) {
+ DIEObject* ret = iter->find_die_object(die_to_find);
+ if (ret != NULL) {
+ return ret;
+ }
+ iter = iter->prev_sibling();
+ }
+
+ _set_errno(EINVAL);
+ return NULL;
+}
+
+void DIEObject::dump(bool only_this) const {
+ const Dwarf_Abbr_AT* at_abbr;
+ Dwarf_Tag tag;
+
+ const Elf_Byte* die_attr = advance(&at_abbr, &tag);
+ if (die_attr != NULL) {
+ printf("\n********** DIE[%p(%04X)] %s: %s **********\n",
+ die_, parent_cu()->get_die_reference(die_), dwarf_tag_name(tag),
+ get_name());
+
+ /* Dump this DIE attributes. */
+ while (!at_abbr->is_separator()) {
+ DIEAttrib attr;
+ at_abbr = at_abbr->process(&attr.at_, &attr.form_);
+ die_attr = parent_cu()->process_attrib(die_attr, attr.form(), &attr.value_);
+ dump_attrib(attr.at(), attr.form(), attr.value());
+ if (attr.at() == DW_AT_ranges) {
+ /* Dump all ranges for this DIE. */
+ Elf_Word off = attr.value()->u32;
+ if (parent_cu()->is_CU_address_64()) {
+ Elf_Xword low, high;
+ while (elf_file()->get_range<Elf_Xword>(off, &low, &high) &&
+ (low != 0 || high != 0)) {
+ printf(" %08I64X - %08I64X\n",
+ low, high);
+ off += 16;
+ }
+ } else {
+ Elf_Word low, high;
+ while (elf_file()->get_range<Elf_Word>(off, &low, &high) &&
+ (low != 0 || high != 0)) {
+ printf(" %08X - %08X\n",
+ low, high);
+ off += 8;
+ }
+ }
+ }
+ }
+ }
+
+ if (only_this) {
+ if (parent_die_ != NULL && !parent_die_->is_cu_die()) {
+ printf("\n-----------> CHILD OF:\n");
+ parent_die_->dump(true);
+ }
+ } else {
+ /* Dump this DIE's children. */
+ if (last_child() != NULL) {
+ last_child()->dump(false);
+ }
+
+ /* Dump this DIE's siblings. */
+ if (prev_sibling() != NULL) {
+ prev_sibling()->dump(false);
+ }
+ }
+}
+
+const Elf_Byte* DIEObject::advance(const Dwarf_Abbr_AT** at_abbr,
+ Dwarf_Tag* tag) const {
+ Dwarf_AbbrNum abbr_num;
+ Dwarf_Tag die_tag;
+
+ const Elf_Byte* die_attr = die()->process(&abbr_num);
+ const Dwarf_Abbr_DIE* abbr = parent_cu()->get_die_abbr(abbr_num);
+ if (abbr == NULL) {
+ return NULL;
+ }
+
+ const Dwarf_Abbr_AT* attrib_abbr = abbr->process(NULL, &die_tag);
+ if (at_abbr != NULL) {
+ *at_abbr = attrib_abbr;
+ }
+ if (tag != NULL) {
+ *tag = die_tag;
+ }
+ return die_attr;
+}
diff --git a/elff/dwarf_die.h b/elff/dwarf_die.h new file mode 100644 index 0000000..21dea37 --- /dev/null +++ b/elff/dwarf_die.h @@ -0,0 +1,202 @@ +/* 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 classes defined for a variety of DWARF objects.
+ */
+
+#ifndef ELFF_DWARF_DIE_H_
+#define ELFF_DWARF_DIE_H_
+
+#include "dwarf_defs.h"
+#include "elf_alloc.h"
+
+class ElfFile;
+class DwarfCU;
+
+/* Encapsulates an object that wraps up a DIE, cached during
+ * ELF file parsing.
+ */
+class DIEObject : public DwarfAllocBase {
+ public:
+ /* Constructs DIEObject intance.
+ * Param:
+ * die - DIE represented with this instance.
+ * parent_cu - Compilation unit this DIE belongs to.
+ * parent_die - Parent DIE object for this DIE. This parameter can be NULL
+ * only for compilation unit DIEs.
+ */
+ DIEObject(const Dwarf_DIE* die, DwarfCU* parent_cu, DIEObject* parent_die)
+ : die_(die),
+ parent_cu_(parent_cu),
+ parent_die_(parent_die),
+ prev_sibling_(NULL),
+ last_child_(NULL) {
+ }
+
+ /* Destructs DIEObject intance. */
+ ~DIEObject();
+
+ /* Gets ELF file this DIE belongs to. */
+ ElfFile* elf_file() const;
+
+ /* Gets DWARF tag (DW_TAG_Xxx) for the DIE represented with this instance. */
+ Dwarf_Tag get_tag() const;
+
+ /* Gets the best name for this DIE.
+ * Some DIEs (such as inline routine DIEs) may have no DW_AT_name property,
+ * but may reference to another DIE that may contain DIE name. This method
+ * tries its best to get DIE name by iterating through different methods of
+ * naming the DIE.
+ * Return:
+ * Name for this DIE, or NULL if it was not possible to find a relevant DIE
+ * with DW_AT_name property.
+ */
+ const char* get_name() const;
+
+ /* Gets DIE's attribute by its ID.
+ * Param:
+ * at_id - ID (DW_AT_Xxx) of the attribute to get.
+ * attr - Upon successful return contains requested attribute information.
+ * Return:
+ * true on success, or false if attribute for the given ID doesn't exist
+ * in the DIE's attribute list.
+ */
+ bool get_attrib(Dwarf_At at, DIEAttrib* attr) const;
+
+ /* Gets the leaf DIE object containing given address.
+ * See DwarfCU::get_leaf_die_for_address() for method details.
+ * See DIEObject::contains_address() for implementation details.
+ */
+ DIEObject* get_leaf_for_address(Elf_Xword address);
+
+ /* Finds a DIE object for the given die in the branch starting with
+ * this DIE object.
+ */
+ DIEObject* find_die_object(const Dwarf_DIE* die_to_find);
+
+ /* Dumps this object to stdout.
+ * Param:
+ * only_this - If true, only this object will be dumped. If this parameter
+ * is false, all the childs and siblings of this object will be dumped
+ * along with this object.
+ */
+ void dump(bool only_this) const;
+
+ protected:
+ /* Checks if this DIE object containing given address.
+ * Template param:
+ * AddrType - Type of compilation unin address (4, or 8 bytes), defined by
+ * address_size field of the CU header. Must be Elf_Xword for 8 bytes
+ * address, or Elf_Word for 4 bytes address.
+ * Param:
+ * address - Address ti check.
+ * Return:
+ * True, if this DIE address ranges (including low_pc, high_pc attributes)
+ * contain given address, or false otherwise.
+ */
+ template <typename AddrType>
+ bool contains_address(Elf_Xword address);
+
+ /* Advances to the DIE's property list.
+ * Param:
+ * at_abbr - Upon successful return contains a pointer to the beginning of
+ * DIE attribute abbreviation list. This parameter can be NULL, if the
+ * caller is not interested in attribute abbreviation list for this DIE.
+ * tag - Upon successful return contains DIE's tag. This parameter can be
+ * NULL, if the caller is not interested in the tag value for this DIE.
+ * Return:
+ * Pointer to the beginning of the DIE attribute list in mapped .debug_info
+ * section on success, or NULL on failure.
+ */
+ const Elf_Byte* advance(const Dwarf_Abbr_AT** at_abbr, Dwarf_Tag* tag) const;
+
+ public:
+ /* Gets DIE represented with this instance. */
+ const Dwarf_DIE* die() const {
+ return die_;
+ }
+
+ /* Gets compilation unit this DIE belongs to. */
+ DwarfCU* parent_cu() const {
+ return parent_cu_;
+ }
+
+ /* Gets parent DIE object for this die. */
+ DIEObject* parent_die() const {
+ return parent_die_;
+ }
+
+ /* Gets last child object in the list of this DIE's childs. NOTE: for better
+ * performace the list is created in reverse order (relatively to the order,
+ * in which children DIEs have been discovered).
+ */
+ DIEObject* last_child() const {
+ return last_child_;
+ }
+
+ /* Links next child to the list of this DIE childs. */
+ void link_child(DIEObject* child) {
+ last_child_ = child;
+ }
+
+ /* Gets previous sibling of this DIE in the parent's DIE object list. */
+ DIEObject* prev_sibling() const {
+ return prev_sibling_;
+ }
+
+ /* Links next sibling to the list of this DIE siblings. */
+ void link_sibling(DIEObject* sibl) {
+ prev_sibling_ = sibl;
+ }
+
+ /* Checks if this DIE object represents a CU DIE.
+ * We relay here on the fact that only CU DIE objects have no parent
+ * DIE objects.
+ */
+ bool is_cu_die() const {
+ return parent_die_ == NULL;
+ }
+
+ /* Gets this DIE level in the branch.
+ * DIE level defines DIE's distance from the CU DIE in the branch this DIE
+ * belongs to. In other words, DIE level defines how many parent DIEs exist
+ * between this DIE, and the CU DIE. For instance, the CU DIE has level 0,
+ * a subroutine a() in this compilation unit has level 1, a soubroutine b(),
+ * that has been inlined into subroutine a() will have level 2, a try/catch
+ * block in the inlined subroutine b() will have level 3, and so on.
+ */
+ Elf_Word get_level() const {
+ return parent_die_ != NULL ? parent_die_->get_level() + 1 : 0;
+ }
+
+ protected:
+ /* DIE that is represented with this instance. */
+ const Dwarf_DIE* die_;
+
+ /* Compilation unit this DIE belongs to. */
+ DwarfCU* parent_cu_;
+
+ /* Parent DIE object for this die. */
+ DIEObject* parent_die_;
+
+ /* Last child object in the list of this DIE's childs. NOTE: for better
+ * performace the list is created in reverse order (relatively to the order,
+ * in which children DIEs have been discovered).
+ */
+ DIEObject* last_child_;
+
+ /* Previous sibling of this DIE in the parent's DIE object list. */
+ DIEObject* prev_sibling_;
+};
+
+#endif // ELFF_DWARF_DIE_H_
diff --git a/elff/dwarf_utils.cc b/elff/dwarf_utils.cc new file mode 100644 index 0000000..56c05a3 --- /dev/null +++ b/elff/dwarf_utils.cc @@ -0,0 +1,313 @@ +/* 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 misc. DWARF utility routines. + */ + +#include "stdio.h" +#include "dwarf_utils.h" + +/* "Stringifies" the parameter. */ +#define DWARF_NAMEFY(val) case val: return "" #val "" + +/* "Stringifies" two parameters. */ +#define DWARF_NAMEFY2(val1, val2) case val1: return "" #val1 " | " #val2 "" + +const char* +dwarf_at_name(Dwarf_At at) { + switch (at) { + DWARF_NAMEFY(DW_AT_sibling); + DWARF_NAMEFY(DW_AT_location); + DWARF_NAMEFY(DW_AT_name); + DWARF_NAMEFY(DW_AT_ordering); + DWARF_NAMEFY(DW_AT_subscr_data); + DWARF_NAMEFY(DW_AT_byte_size); + DWARF_NAMEFY(DW_AT_bit_offset); + DWARF_NAMEFY(DW_AT_bit_size); + DWARF_NAMEFY(DW_AT_element_list); + DWARF_NAMEFY(DW_AT_stmt_list); + DWARF_NAMEFY(DW_AT_low_pc); + DWARF_NAMEFY(DW_AT_high_pc); + DWARF_NAMEFY(DW_AT_language); + DWARF_NAMEFY(DW_AT_member); + DWARF_NAMEFY(DW_AT_discr); + DWARF_NAMEFY(DW_AT_discr_value); + DWARF_NAMEFY(DW_AT_visibility); + DWARF_NAMEFY(DW_AT_import); + DWARF_NAMEFY(DW_AT_string_length); + DWARF_NAMEFY(DW_AT_common_reference); + DWARF_NAMEFY(DW_AT_comp_dir); + DWARF_NAMEFY(DW_AT_const_value); + DWARF_NAMEFY(DW_AT_containing_type); + DWARF_NAMEFY(DW_AT_default_value); + DWARF_NAMEFY(DW_AT_inline); + DWARF_NAMEFY(DW_AT_is_optional); + DWARF_NAMEFY(DW_AT_lower_bound); + DWARF_NAMEFY(DW_AT_producer); + DWARF_NAMEFY(DW_AT_prototyped); + DWARF_NAMEFY(DW_AT_return_addr); + DWARF_NAMEFY(DW_AT_start_scope); + DWARF_NAMEFY2(DW_AT_bit_stride, DW_AT_stride_size); + DWARF_NAMEFY(DW_AT_upper_bound); + DWARF_NAMEFY(DW_AT_abstract_origin); + DWARF_NAMEFY(DW_AT_accessibility); + DWARF_NAMEFY(DW_AT_address_class); + DWARF_NAMEFY(DW_AT_artificial); + DWARF_NAMEFY(DW_AT_base_types); + DWARF_NAMEFY(DW_AT_calling_convention); + DWARF_NAMEFY(DW_AT_count); + DWARF_NAMEFY(DW_AT_data_member_location); + DWARF_NAMEFY(DW_AT_decl_column); + DWARF_NAMEFY(DW_AT_decl_file); + DWARF_NAMEFY(DW_AT_decl_line); + DWARF_NAMEFY(DW_AT_declaration); + DWARF_NAMEFY(DW_AT_discr_list); + DWARF_NAMEFY(DW_AT_encoding); + DWARF_NAMEFY(DW_AT_external); + DWARF_NAMEFY(DW_AT_frame_base); + DWARF_NAMEFY(DW_AT_friend); + DWARF_NAMEFY(DW_AT_identifier_case); + DWARF_NAMEFY(DW_AT_macro_info); + DWARF_NAMEFY(DW_AT_namelist_item); + DWARF_NAMEFY(DW_AT_priority); + DWARF_NAMEFY(DW_AT_segment); + DWARF_NAMEFY(DW_AT_specification); + DWARF_NAMEFY(DW_AT_static_link); + DWARF_NAMEFY(DW_AT_type); + DWARF_NAMEFY(DW_AT_use_location); + DWARF_NAMEFY(DW_AT_variable_parameter); + DWARF_NAMEFY(DW_AT_virtuality); + DWARF_NAMEFY(DW_AT_vtable_elem_location); + DWARF_NAMEFY(DW_AT_allocated); + DWARF_NAMEFY(DW_AT_associated); + DWARF_NAMEFY(DW_AT_data_location); + DWARF_NAMEFY2(DW_AT_byte_stride, DW_AT_stride); + DWARF_NAMEFY(DW_AT_entry_pc); + DWARF_NAMEFY(DW_AT_use_UTF8); + DWARF_NAMEFY(DW_AT_extension); + DWARF_NAMEFY(DW_AT_ranges); + DWARF_NAMEFY(DW_AT_trampoline); + DWARF_NAMEFY(DW_AT_call_column); + DWARF_NAMEFY(DW_AT_call_file); + DWARF_NAMEFY(DW_AT_call_line); + DWARF_NAMEFY(DW_AT_description); + DWARF_NAMEFY(DW_AT_binary_scale); + DWARF_NAMEFY(DW_AT_decimal_scale); + DWARF_NAMEFY(DW_AT_small); + DWARF_NAMEFY(DW_AT_decimal_sign); + DWARF_NAMEFY(DW_AT_digit_count); + DWARF_NAMEFY(DW_AT_picture_string); + DWARF_NAMEFY(DW_AT_mutable); + DWARF_NAMEFY(DW_AT_threads_scaled); + DWARF_NAMEFY(DW_AT_explicit); + DWARF_NAMEFY(DW_AT_object_pointer); + DWARF_NAMEFY(DW_AT_endianity); + DWARF_NAMEFY(DW_AT_elemental); + DWARF_NAMEFY(DW_AT_pure); + DWARF_NAMEFY(DW_AT_recursive); + DWARF_NAMEFY(DW_AT_signature); + DWARF_NAMEFY(DW_AT_main_subprogram); + DWARF_NAMEFY(DW_AT_data_bit_offset); + DWARF_NAMEFY(DW_AT_const_expr); + DWARF_NAMEFY(DW_AT_enum_class); + DWARF_NAMEFY(DW_AT_linkage_name); + default: + return "DW_AT_Unknown"; + } +} + +const char* +dwarf_form_name(Dwarf_Form form) { + switch (form) { + DWARF_NAMEFY(DW_FORM_addr); + DWARF_NAMEFY(DW_FORM_block2); + DWARF_NAMEFY(DW_FORM_block4); + DWARF_NAMEFY(DW_FORM_data2); + DWARF_NAMEFY(DW_FORM_data4); + DWARF_NAMEFY(DW_FORM_data8); + DWARF_NAMEFY(DW_FORM_string); + DWARF_NAMEFY(DW_FORM_block); + DWARF_NAMEFY(DW_FORM_block1); + DWARF_NAMEFY(DW_FORM_data1); + DWARF_NAMEFY(DW_FORM_flag); + DWARF_NAMEFY(DW_FORM_sdata); + DWARF_NAMEFY(DW_FORM_strp); + DWARF_NAMEFY(DW_FORM_udata); + DWARF_NAMEFY(DW_FORM_ref_addr); + DWARF_NAMEFY(DW_FORM_ref1); + DWARF_NAMEFY(DW_FORM_ref2); + DWARF_NAMEFY(DW_FORM_ref4); + DWARF_NAMEFY(DW_FORM_ref8); + DWARF_NAMEFY(DW_FORM_ref_udata); + DWARF_NAMEFY(DW_FORM_indirect); + DWARF_NAMEFY(DW_FORM_sec_offset); + DWARF_NAMEFY(DW_FORM_exprloc); + DWARF_NAMEFY(DW_FORM_flag_present); + DWARF_NAMEFY(DW_FORM_ref_sig8); + default: + return "DW_FORM_Unknown"; + } +} + +const char* +dwarf_tag_name(Dwarf_Tag tag) { + switch (tag) { + DWARF_NAMEFY(DW_TAG_array_type); + DWARF_NAMEFY(DW_TAG_class_type); + DWARF_NAMEFY(DW_TAG_entry_point); + DWARF_NAMEFY(DW_TAG_enumeration_type); + DWARF_NAMEFY(DW_TAG_formal_parameter); + DWARF_NAMEFY(DW_TAG_imported_declaration); + DWARF_NAMEFY(DW_TAG_label); + DWARF_NAMEFY(DW_TAG_lexical_block); + DWARF_NAMEFY(DW_TAG_member); + DWARF_NAMEFY(DW_TAG_pointer_type); + DWARF_NAMEFY(DW_TAG_reference_type); + DWARF_NAMEFY(DW_TAG_compile_unit); + DWARF_NAMEFY(DW_TAG_string_type); + DWARF_NAMEFY(DW_TAG_structure_type); + DWARF_NAMEFY(DW_TAG_subroutine_type); + DWARF_NAMEFY(DW_TAG_typedef); + DWARF_NAMEFY(DW_TAG_union_type); + DWARF_NAMEFY(DW_TAG_unspecified_parameters); + DWARF_NAMEFY(DW_TAG_variant); + DWARF_NAMEFY(DW_TAG_common_block); + DWARF_NAMEFY(DW_TAG_common_inclusion); + DWARF_NAMEFY(DW_TAG_inheritance); + DWARF_NAMEFY(DW_TAG_inlined_subroutine); + DWARF_NAMEFY(DW_TAG_module); + DWARF_NAMEFY(DW_TAG_ptr_to_member_type); + DWARF_NAMEFY(DW_TAG_set_type); + DWARF_NAMEFY(DW_TAG_subrange_type); + DWARF_NAMEFY(DW_TAG_with_stmt); + DWARF_NAMEFY(DW_TAG_access_declaration); + DWARF_NAMEFY(DW_TAG_base_type); + DWARF_NAMEFY(DW_TAG_catch_block); + DWARF_NAMEFY(DW_TAG_const_type); + DWARF_NAMEFY(DW_TAG_constant); + DWARF_NAMEFY(DW_TAG_enumerator); + DWARF_NAMEFY(DW_TAG_file_type); + DWARF_NAMEFY(DW_TAG_friend); + DWARF_NAMEFY(DW_TAG_namelist); + DWARF_NAMEFY2(DW_TAG_namelist_item, DW_TAG_namelist_items); + DWARF_NAMEFY(DW_TAG_packed_type); + DWARF_NAMEFY(DW_TAG_subprogram); + DWARF_NAMEFY2(DW_TAG_template_type_parameter, DW_TAG_template_type_param); + DWARF_NAMEFY2(DW_TAG_template_value_parameter, + DW_TAG_template_value_param); + DWARF_NAMEFY(DW_TAG_thrown_type); + DWARF_NAMEFY(DW_TAG_try_block); + DWARF_NAMEFY(DW_TAG_variant_part); + DWARF_NAMEFY(DW_TAG_variable); + DWARF_NAMEFY(DW_TAG_volatile_type); + DWARF_NAMEFY(DW_TAG_dwarf_procedure); + DWARF_NAMEFY(DW_TAG_restrict_type); + DWARF_NAMEFY(DW_TAG_interface_type); + DWARF_NAMEFY(DW_TAG_namespace); + DWARF_NAMEFY(DW_TAG_imported_module); + DWARF_NAMEFY(DW_TAG_unspecified_type); + DWARF_NAMEFY(DW_TAG_partial_unit); + DWARF_NAMEFY(DW_TAG_imported_unit); + DWARF_NAMEFY(DW_TAG_mutable_type); + DWARF_NAMEFY(DW_TAG_condition); + DWARF_NAMEFY(DW_TAG_shared_type); + DWARF_NAMEFY(DW_TAG_type_unit); + DWARF_NAMEFY(DW_TAG_rvalue_reference_type); + DWARF_NAMEFY(DW_TAG_template_alias); + default: + return "DW_TAG_Unknown"; + } +} + +void +dump_attrib(Dwarf_At at, Dwarf_Form form, const Dwarf_Value* val) { + if (form != 0) { + printf(" +++ Attribute: %s [%s]\n", + dwarf_at_name(at), dwarf_form_name(form)); + } else { + printf(" +++ Attribute: %s\n", dwarf_at_name(at)); + } + dump_value(val); +} + +void +dump_value(const Dwarf_Value* attr_value) { + printf(" Data[%03u]: (", attr_value->encoded_size); + switch (attr_value->type) { + case DWARF_VALUE_U8: + printf("BYTE) = %u (x%02X)\n", (Elf_Word)attr_value->u8, + (Elf_Word)attr_value->u8); + break; + + case DWARF_VALUE_S8: + printf("SBYTE) = %d (x%02X)\n", (Elf_Sword)attr_value->s8, + (Elf_Sword)attr_value->s8); + break; + + case DWARF_VALUE_U16: + printf("WORD) = %u (x%04X)\n", (Elf_Word)attr_value->u16, + (Elf_Word)attr_value->u16); + break; + + case DWARF_VALUE_S16: + printf("SWORD) = %d (x%04X)\n", (Elf_Sword)attr_value->s16, + (Elf_Sword)attr_value->s16); + break; + + case DWARF_VALUE_U32: + printf("DWORD) = %u (x%08X)\n", attr_value->u32, + attr_value->u32); + break; + + case DWARF_VALUE_S32: + printf("SDWORD) = %d (x%08X)\n", attr_value->s32, + attr_value->s32); + break; + + case DWARF_VALUE_U64: + printf("XWORD) = %I64u (x%I64X)\n", attr_value->u64, + attr_value->u64); + break; + + case DWARF_VALUE_S64: + printf("SXWORD) = %I64d (x%I64X)\n", attr_value->s64, + attr_value->s64); + break; + + case DWARF_VALUE_STR: + printf("STRING) = %s\n", attr_value->str); + break; + + case DWARF_VALUE_PTR32: + printf("PTR32) = x%08X\n", attr_value->ptr32); + break; + + case DWARF_VALUE_PTR64: + printf("PTR64) = x%08I64X\n", attr_value->ptr64); + break; + + case DWARF_VALUE_BLOCK: + printf("BLOCK) = [%I64u]:", attr_value->block.block_size); + for (Elf_Xword i = 0; i < attr_value->block.block_size; i++) { + Elf_Byte prnt = *((const Elf_Byte*)attr_value->block.block_ptr + i); + printf(" x%02X", prnt); + } + printf("\n"); + break; + + case DWARF_VALUE_UNKNOWN: + default: + printf("UNKNOWN)"); + break; + } +} diff --git a/elff/dwarf_utils.h b/elff/dwarf_utils.h new file mode 100644 index 0000000..fc007d1 --- /dev/null +++ b/elff/dwarf_utils.h @@ -0,0 +1,63 @@ +/* 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 misc. DWARF utility routines.
+ */
+
+#ifndef ELFF_DWARF_UTILS_
+#define ELFF_DWARF_UTILS_
+
+#include "dwarf_defs.h"
+
+/* Gets DWARF attribute name string (DW_AT_Xxx) for a given attribute ID.
+ * Param:
+ * at - DWARF attribute ID to get name string for.
+ * Return:
+ * Attribute name string. Note that this routine returns "DW_AT_Unknown",
+ * if DWARF attribute value passed to this routine has not been recognized.
+ */
+const char* dwarf_at_name(Dwarf_At at);
+
+/* Gets DWARF form name string (DW_FORM_Xxx) for a given form.
+ * Param:
+ * form - DWARF form to get name string for.
+ * Return:
+ * Form name string. Note that this routine returns "DW_FORM_Unknown", if
+ * DWARF form value passed to this routine has not been recognized.
+ */
+const char* dwarf_form_name(Dwarf_Form form);
+
+/* Gets DWARF tag name string (DW_TAG_Xxx) for a given tag.
+ * Param:
+ * tag - DWARF tag to get name string for.
+ * Return:
+ * Tag name string. Note that this routine returns "DW_TAG_Unknown", if DWARF
+ * tag value passed to this routine has not been recognized.
+ */
+const char* dwarf_tag_name(Dwarf_Tag tag);
+
+/* Dumps DWARF attribute to stdout.
+ * Param:
+ * at - Attribute ID (DW_AT_Xxx)
+ * form - Attribute form (DW_FORM_Xxx)
+ * val - Attribute value.
+ */
+void dump_attrib(Dwarf_At at, Dwarf_Form form, const Dwarf_Value* val);
+
+/* Dumps DWARF attribute value to stdout.
+ * Param:
+ * val - Attribute value.
+ */
+void dump_value(const Dwarf_Value* val);
+
+#endif // ELFF_DWARF_UTILS_
diff --git a/elff/elf.h b/elff/elf.h new file mode 100644 index 0000000..de92ba6 --- /dev/null +++ b/elff/elf.h @@ -0,0 +1,299 @@ +/* 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 structures + * describing ELF file format. + */ + +#ifndef ELFF_ELH_H_ +#define ELFF_ELH_H_ + +extern "C" { +#include "qemu-common.h" +} +#include "elff-common.h" + +//============================================================================= +// ELF file definitions +//============================================================================= + +#ifdef WIN32 +typedef HANDLE ELF_FILE_HANDLE; +#define INVALID_ELF_FILE_HANDLE INVALID_HANDLE_VALUE +#define close_elf_file_handle CloseHandle +#else // WIN32 +typedef int ELF_FILE_HANDLE; +#define INVALID_ELF_FILE_HANDLE -1 +#define close_elf_file_handle close +#endif // WIN32 + +/* Checks if ELF file handle is valid. */ +static inline bool +elfhandle_is_valid(ELF_FILE_HANDLE handle) { + return handle != INVALID_ELF_FILE_HANDLE; +} + +/* + * ELF format documentation uses Elf##_Xxx notation for data types, where + * ## stands for CPU architecture (32, or 64 bit), and Xxx stands for a + * specific type. For the sake of compliance, we will follow doc's notation + * when defining types used in ELF file descriptors. However, for the sake of + * code simplicity, we will drop CPU architecture index from the types that + * have equal sizes on both, 32 and 64 bit architectures. + */ + +/* + * Architecture independent types. + */ + +typedef uint8_t Elf_Byte; +typedef int8_t Elf_Sbyte; + +typedef uint16_t Elf_Half; +typedef int16_t Elf_Shalf; + +typedef uint32_t Elf_Word; +typedef int32_t Elf_Sword; + +typedef uint64_t Elf_Xword; +typedef int64_t Elf_Sxword; + +/* + * Architecture dependent types. + */ + +/* 32-bit ELF address. */ +typedef uint32_t Elf32_Addr; +/* 32-bit ELF offset. */ +typedef uint32_t Elf32_Off; + +/* 64-bit ELF address. */ +typedef uint64_t Elf64_Addr; +/* 64-bit ELF offset. */ +typedef uint64_t Elf64_Off; + +//============================================================================= +// ELF file header +//============================================================================= + +/* Byte size of the fixed portion of ELF header. */ +#define EI_NIDENT 16 + +/* Common (architecture independent portion of) ELF file header, + * that starts at offset 0 in ELF file. + */ +typedef struct Elf_CommonHdr { + union { + struct { + /* ei_mag0 - ei_mag3 contain ELF header signature. See ELFMAGx bellow. */ + Elf_Byte ei_mag0; + Elf_Byte ei_mag1; + Elf_Byte ei_mag2; + Elf_Byte ei_mag3; + + /* File class (32, or 64 bits). See ELFCLASSxxx bellow. */ + Elf_Byte ei_class; + + /* Data encoding (endianness). See ELFDATAxxx bellow. */ + Elf_Byte ei_data; + + /* ELF header version number. */ + Elf_Byte ei_version; + } ei_info; + unsigned char e_ident[EI_NIDENT]; + }; + + /* File type (executable, shared object, etc.) */ + Elf_Half e_type; + + /* Processor type. */ + Elf_Half e_machine; + + /* File version. */ + Elf_Word e_version; +} Elf_CommonHdr; + + +/* ELF header signature. */ +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +/* + * Possible ei_class values. + */ + +/* Invalid. */ +#define ELFCLASSNONE 0 +/* It's 32-bit ELF file. */ +#define ELFCLASS32 1 +/* It's 64-bit ELF file. */ +#define ELFCLASS64 2 + +/* + * Possible ei_data values. + */ + +/* Invalid. */ +#define ELFDATANONE 0 +/* ELF data is formatted in little-endian. */ +#define ELFDATA2LSB 1 +/* ELF data is formatted in big-endian. */ +#define ELFDATA2MSB 2 + +/* Tempated (architecture dependent) ELF file header. + * Template param: + * Elf_Addr - Actual type for address encoding (Elf32_Addr, or Elf64_Addr). + * Elf_Off - Actual type for offset encoding (Elf32_Off, or Elf64_Off). + */ +template <typename Elf_Addr, typename Elf_Off> +struct Elf_FHdr { + /* Common header. */ + Elf_CommonHdr common; + + /* Module entry point. */ + Elf_Addr e_entry; + + /* Programm header table offset (in bytes) from the beginning of the file. + * Zero if there is no programm header in this file. + */ + Elf_Off e_phoff; + + /* Section header table offset (in bytes) from the beginning of the file. + * Zero if there is no section header in this file. + */ + Elf_Off e_shoff; + + /* Processor-specific flags. */ + Elf_Word e_flags; + + /* This header size in bytes. */ + Elf_Half e_ehsize; + + /* Byte size of an entry in programm header table. All entries + * in the table are the same size. + */ + Elf_Half e_phentsize; + + /* Number of entries in programm header table. */ + Elf_Half e_phnum; + + /* Byte size of an entry in section header table. All entries + * in the table are the same size. + */ + Elf_Half e_shentsize; + + /* Number of entries in section header table. */ + Elf_Half e_shnum; + + /* Zero-based index of an entry for name string table section in the section + * header table. If no such section exists in the file this field contains + * SHN_UNDEF value. + */ + Elf_Half e_shstrndx; +}; +/* 32-bit ELF header. */ +typedef Elf_FHdr<Elf32_Addr, Elf32_Off> Elf32_FHdr; +/* 64-bit ELF header. */ +typedef Elf_FHdr<Elf64_Addr, Elf64_Off> Elf64_FHdr; + +//============================================================================= +// ELF section header +//============================================================================= + +/* Templated (architecture dependent) section header for ELF file. + * Template param: + * Elf_Addr - Actual type for address encoding (Elf32_Addr, or Elf64_Addr). + * Elf_Off - Actual type for offset encoding (Elf32_Off, or Elf64_Off). + */ +template <typename Elf_Addr, typename Elf_Off> +struct Elf_SHdr { + /* Index (byte offset) of section name in the name string table section. */ + Elf_Word sh_name; + + /* Section type and semantics. */ + Elf_Word sh_type; + + /* Section flags and attributes. */ + Elf_Word sh_flags; + + /* Section address in the memory image of the process. */ + Elf_Addr sh_addr; + + /* Byte offset from the beginning of the ELF file to the first + * byte in the section. + */ + Elf_Off sh_offset; + + /* Section size in bytes. */ + Elf_Word sh_size; + + /* Section header table index link. Depends on section type. */ + Elf_Word sh_link; + + /* Extra section information, depending on the section type. */ + Elf_Word sh_info; + + /* Address alignment constrains. 0 and 1 means that section has no + * alignment constrains. + */ + Elf_Word sh_addralign; + + /* Entry size for sections that hold some kind of a table. */ + Elf_Word sh_entsize; +}; +/* 32-bit section header. */ +typedef Elf_SHdr<Elf32_Addr, Elf32_Off> Elf32_SHdr; +/* 64-bit section header. */ +typedef Elf_SHdr<Elf64_Addr, Elf64_Off> Elf64_SHdr; + +/* + * Special section indices + */ +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_LOOS 0xff20 +#define SHN_HIOS 0xff3f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_XINDEX 0xffff +#define SHN_HIRESERVE 0xffff + +/* + * Values for sh_type + */ +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_INIT_ARRAY 14 +#define SHT_FINI_ARRAY 15 +#define SHT_PREINIT_ARRAY 16 +#define SHT_GROUP 17 +#define SHT_SYMTAB_SHNDX 18 +#define SHT_NUM 19 + +#endif // ELFF_ELH_H_ diff --git a/elff/elf_alloc.cc b/elff/elf_alloc.cc new file mode 100644 index 0000000..10d740b --- /dev/null +++ b/elff/elf_alloc.cc @@ -0,0 +1,62 @@ +/* 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 class ElfAllocator, that implements memory
+ * allocations for DWARF objects.
+ */
+
+#include "elf_alloc.h"
+#include "elf_file.h"
+
+ElfAllocator::ElfAllocator()
+ : current_chunk_(NULL) {
+}
+
+ElfAllocator::~ElfAllocator() {
+ ElfAllocatorChunk* chunk_to_free = current_chunk_;
+ while (chunk_to_free != NULL) {
+ ElfAllocatorChunk* next_chunk = chunk_to_free->prev;
+ free(chunk_to_free);
+ chunk_to_free = next_chunk;
+ }
+}
+
+void* ElfAllocator::alloc(size_t size) {
+ /* Lets keep everyting properly aligned. */
+ size = (size + ELFALLOC_ALIGNMENT_MASK) & ~ELFALLOC_ALIGNMENT_MASK;
+
+ if (current_chunk_ == NULL || current_chunk_->remains < size) {
+ /* Allocate new chunk. */
+ ElfAllocatorChunk* new_chunk =
+ reinterpret_cast<ElfAllocatorChunk*>(malloc(ELF_ALLOC_CHUNK_SIZE));
+ assert(new_chunk != NULL);
+ if (new_chunk == NULL) {
+ _set_errno(ENOMEM);
+ return NULL;
+ }
+ new_chunk->size = ELF_ALLOC_CHUNK_SIZE;
+ new_chunk->avail = INC_PTR(new_chunk, sizeof(ElfAllocatorChunk));
+ new_chunk->remains = new_chunk->size - sizeof(ElfAllocatorChunk);
+ new_chunk->prev = current_chunk_;
+ current_chunk_ = new_chunk;
+ }
+
+ void* ret = current_chunk_->avail;
+ current_chunk_->remains -= size;
+ current_chunk_->avail = INC_PTR(current_chunk_->avail, size);
+ return ret;
+}
+
+void* DwarfAllocBase::operator new(size_t size, const ElfFile* elf) {
+ return elf->allocator()->alloc(size);
+}
diff --git a/elff/elf_alloc.h b/elff/elf_alloc.h new file mode 100644 index 0000000..d76dcdb --- /dev/null +++ b/elff/elf_alloc.h @@ -0,0 +1,160 @@ +/* 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 declaration of class ElfAllocator, that implements memory
+ * allocations for DWARF objects.
+ */
+
+#ifndef ELFF_ELF_ALLOC_H_
+#define ELFF_ELF_ALLOC_H_
+
+extern "C" {
+#include "qemu-common.h"
+}
+#include "elff-common.h"
+
+class ElfFile;
+
+/* Alignment mask for blocks, allocated with this allocator. */
+#define ELFALLOC_ALIGNMENT_MASK 3
+
+/* Chunk size. Even on relatively small ELF files, there are a lot of DWARF
+ * info, which makes our parsing pretty hungry on memory. On average, memory
+ * consumption on cached DWARF objects may easily reach 640K, which makes
+ * choosing 32K as chunk size pretty reasonable.
+ */
+#define ELF_ALLOC_CHUNK_SIZE (32 * 1024)
+
+/* Describes a chunk of memory, allocated by ElfAllocator.
+ * NOTE: this header's sizeof must be always aligned accordingly to the
+ * ELFALLOC_ALIGNMENT_MASK value, so we can produce properly aligned blocks
+ * without having to adjust alignment of the blocks, returned from alloc()
+ * method.
+ */
+typedef struct ElfAllocatorChunk {
+ /* Previous chunk in the chain of chunks allocated by ElfAllocator instance.
+ * For better allocation performance, ElfAllocator keeps its list of
+ * allocated chunks in reverse order (relatively to the chunk allocation
+ * sequence). So this field in each chunk references the chunk, allocated
+ * just prior this one. This field contains NULL for the first allocated
+ * chunk.
+ */
+ ElfAllocatorChunk* prev;
+
+ /* Address of the next available block in this chunk. */
+ void* avail;
+
+ /* Chunk size. */
+ size_t size;
+
+ /* Number of bytes that remain available in this chunk. */
+ size_t remains;
+} ElfAllocatorChunk;
+
+/* Encapsulates memory allocator for DWARF-related objects.
+ * Due to the implementation of ELF/DWARF framework in this library, data,
+ * collected during ELF/DWARF parsing stays in memory for as long, as instance
+ * of ElfFile that's being parsed is alive. To save performance on the numerous
+ * memory allocations (and then, deallocations) we will use this simple memory
+ * allocator that will grab memory from the heap in large chunks and then will
+ * provide DWARF objects with blocks of the required size inside those chunks.
+ * This will be much faster than going to the heap all the time, and since we
+ * will use overwritten operators new/delete for the DWARF objects that use
+ * this allocator, this is going to be pretty flexible and reliable solution
+ * for DWARF object allocation implementation. See DwarfAllocBase for more
+ * details.
+ *
+ * Instance (always one) of this class is created by ElfFile object when it is
+ * initializing.
+ */
+class ElfAllocator {
+ public:
+ /* Constructs ElfAllocator instance. */
+ ElfAllocator();
+
+ /* Destructs ElfAllocator instance. */
+ ~ElfAllocator();
+
+ /* Allocates requested number of bytes for a DWARF object.
+ * Param:
+ * size - Number of bytes to allocate. Value passed in this parameter
+ * will be rounded up accordingly to ELFALLOC_ALIGNMENT_MASK value,
+ * simplifying alignment adjustments for the allocated blocks.
+ * Return:
+ * Address of allocated block of the requested size on success,
+ * or NULL on failure.
+ */
+ void* alloc(size_t size);
+
+ protected:
+ /* Current chunk to allocate memory from. NOTE: chunks are listed here
+ * in reverse order (relatively to the chunk allocation sequence).
+ */
+ ElfAllocatorChunk* current_chunk_;
+};
+
+/* Base class for all WDARF objects that will use ElfAllocator class for
+ * instance allocations. NOTE: it's required, that all classes that use
+ * ElfAllocator are derived from this one, as it provides compilation-time
+ * protection from mistakenly using "traditional" operator 'new' for object
+ * instantiation.
+ */
+class DwarfAllocBase {
+ public:
+ /* Constructs DwarfAllocBase instance. */
+ DwarfAllocBase() {
+ }
+
+ /* Destructs DwarfAllocBase instance. */
+ virtual ~DwarfAllocBase() {
+ }
+
+ /* Main operator new.
+ * Implements allocation of objects of derived classes from elf's "chunked"
+ * allocator, instantiated in ElfFile object (see ElfAllocator class).
+ * Param:
+ * size - Number of bytes to allocate for an instance of the derived class.
+ * elf - ELF file instance that owns the allocating object.
+ * Return:
+ * Pointer to the allocated memory on success, or NULL on failure.
+ */
+ void* operator new(size_t size, const ElfFile* elf);
+
+ /* Overwitten operator delete.
+ * Since deleting for chunk-allocated objects is a "no-op", we don't do
+ * anything in this operator. We, however, are obliged to implement this
+ * operator in order to compliment overwritten operator 'new'.
+ */
+ void operator delete(void* ptr) {
+ }
+
+ /* Overwitten operator delete.
+ * Since deleting for chunk-allocated objects is a "no-op", we don't do
+ * anything in this operator. We, however, are obliged to implement this
+ * operator in order to compliment overwritten operator 'new'.
+ */
+ void operator delete[](void* ptr) {
+ }
+
+ private:
+ /* Default operator new.
+ * We override it making 'private' in order to cause a compiler error on
+ * attempts to instantiate objects of derived classes using this version
+ * of operator 'new'.
+ */
+ void* operator new(size_t size) {
+ return NULL;
+ }
+};
+
+#endif // ELFF_ELF_ALLOC_H_
diff --git a/elff/elf_defs.h b/elff/elf_defs.h new file mode 100644 index 0000000..39fd1b1 --- /dev/null +++ b/elff/elf_defs.h @@ -0,0 +1,135 @@ +/* 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 some helpful macros, and inline routines.
+ */
+
+#ifndef ELFF_ELF_DEFS_H_
+#define ELFF_ELF_DEFS_H_
+
+#include "elf.h"
+
+//=============================================================================
+// Macros.
+//=============================================================================
+
+/* Increments a pointer by n bytes.
+ * Param:
+ * p - Pointer to increment.
+ * n - Number of bytes to increment the pointer with.
+ */
+#define INC_PTR(p, n) (reinterpret_cast<uint8_t*>(p) + (n))
+
+/* Increments a constant pointer by n bytes.
+ * Param:
+ * p - Pointer to increment.
+ * n - Number of bytes to increment the pointer with.
+ */
+#define INC_CPTR(p, n) (reinterpret_cast<const uint8_t*>(p) + (n))
+
+/* Increments a pointer of a given type by n bytes.
+ * Param:
+ * T - Pointer type
+ * p - Pointer to increment.
+ * n - Number of bytes to increment the pointer with.
+ */
+#define INC_PTR_T(T, p, n) \
+ reinterpret_cast<T*> \
+ (reinterpret_cast<uint8_t*>(p) + (n))
+
+/* Increments a constant pointer of a given type by n bytes.
+ * Param:
+ * T - Pointer type
+ * p - Pointer to increment.
+ * n - Number of bytes to increment the pointer with.
+ */
+#define INC_CPTR_T(T, p, n) \
+ reinterpret_cast<const T*> \
+ (reinterpret_cast<const uint8_t*>(p) + (n))
+
+/* Calculates number of entries in a static array.
+ * Param:
+ * a - Array.
+ * Return:
+ * Number of entries in the array.
+ */
+#define ELFF_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
+/* Calculates offset of a field inside a structure (or a class) of the
+ * given type.
+ * Param:
+ * T - Structure (or class) type.
+ * f - Name of a field (member variable) for this structure (or class).
+ */
+#define ELFF_FIELD_OFFSET(T, f) ((size_t)(size_t*)&(((T *)0)->f))
+
+//=============================================================================
+// Inline routines.
+//=============================================================================
+
+/* Calculates byte interval between two pointers.
+ * Param:
+ * s - Starting pointer of the interval. Must be less, or equal to 'e'.
+ * e - Ending pointer of the interval. Must be greater, or equal to 's'.
+ * Return:
+ * Byte interval between two pointers.
+ */
+static inline size_t
+diff_ptr(const void* s, const void* e) {
+ assert(s <= e);
+ return ((size_t)(reinterpret_cast<const uint8_t*>(e) -
+ reinterpret_cast<const uint8_t*>(s)));
+}
+
+/* Gets one byte from an index inside a memory block.
+ * Param:
+ * ptr - Address of the beginning of the memory block.
+ * bt - Index of a byte inside the block to get.
+ * Return:
+ * A byte at the given index inside the given memory block.
+ */
+static inline uint8_t
+get_byte(const void* ptr, uint32_t bt) {
+ return *(reinterpret_cast<const uint8_t*>(ptr) + bt);
+}
+
+/* Checks if given address range is fully contained within a section.
+ * Param:
+ * rp - Beginning of the range to check.
+ * rsize - Size of the range to check.
+ * ss - Beginning of the section that should contain the checking range.
+ * ssize - Size of the section that should contain the checking range.
+ * Return:
+ * true, if given address range is fully contained within a section, or
+ * false, if any part of the address range is not contained in the secton.
+ */
+static inline bool
+is_in_section(const void* rp, size_t rsize, const void* ss, size_t ssize) {
+ const void* rend = INC_CPTR(rp, rsize);
+ /* We also make sure here that increment didn't overflow the pointer. */
+ return rp >= ss && ss != NULL && (diff_ptr(ss, rend) <= ssize) && rend >= rp;
+}
+
+/* Checks if this code runs on CPU with a little-endian data format.
+ * Return:
+ * true, if this code runs on CPU with a little-endian data format,
+ * or false, if this code runs on CPU with a big-endian data format.
+ */
+static inline bool
+is_little_endian_cpu(void) {
+ uint16_t tmp = 0x00FF;
+ /* Lets see if byte has flipped for little-endian. */
+ return get_byte(&tmp, 0) == 0xFF;
+}
+
+#endif // ELFF_ELF_DEFS_H_
diff --git a/elff/elf_file.cc b/elff/elf_file.cc new file mode 100644 index 0000000..70ed81c --- /dev/null +++ b/elff/elf_file.cc @@ -0,0 +1,539 @@ +/* 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 ElfFile classes that encapsulate an ELF file.
+ */
+
+#include "string.h"
+#include "elf_file.h"
+#include "elf_alloc.h"
+#include "dwarf_cu.h"
+#include "dwarf_utils.h"
+
+/* Tags to parse when collecting info about routines. */
+static const Dwarf_Tag parse_rt_tags[] = {
+ DW_TAG_compile_unit,
+ DW_TAG_partial_unit,
+ DW_TAG_inlined_subroutine,
+ DW_TAG_subprogram,
+ 0
+};
+static const DwarfParseContext parse_rt_context = { parse_rt_tags };
+
+//=============================================================================
+// Base ElfFile implementation
+//=============================================================================
+
+ElfFile::ElfFile()
+ : sec_table_(NULL),
+ elf_file_path_(NULL),
+ sec_count_(0),
+ cu_count_(0),
+ last_cu_(NULL),
+ allocator_(NULL),
+ fixed_base_address_(0),
+ is_exec_(0),
+ elf_handle_(INVALID_ELF_FILE_HANDLE),
+ sec_entry_size_(0) {
+}
+
+ElfFile::~ElfFile() {
+ DwarfCU* cu_to_del = last_cu_;
+ while (cu_to_del != NULL) {
+ DwarfCU* next_cu_to_del = cu_to_del->prev_cu_;
+ delete cu_to_del;
+ cu_to_del = next_cu_to_del;
+ }
+
+ if (elfhandle_is_valid(elf_handle_)) {
+ close_elf_file_handle(elf_handle_);
+ }
+
+ if (elf_file_path_ != NULL) {
+ delete[] elf_file_path_;
+ }
+
+ if (sec_table_ != NULL) {
+ delete[] reinterpret_cast<Elf_Byte*>(sec_table_);
+ }
+
+ /* Must be deleted last! */
+ if (allocator_ != NULL) {
+ delete allocator_;
+ }
+}
+
+ElfFile* ElfFile::Create(const char* path) {
+ ElfFile* ret = NULL;
+ /* Allocate enough space on the stack to fit the largest ELF file header. */
+ Elf64_FHdr header;
+ const Elf_CommonHdr* elf_hdr = &header.common;
+
+ assert(path != NULL && *path != '\0');
+ if (path == NULL || *path == '\0') {
+ _set_errno(EINVAL);
+ return NULL;
+ }
+
+ /*
+ * Open ELF file, and read its header (the largest one possible).
+ */
+
+#ifdef WIN32
+ HANDLE file_handle = CreateFile(path, GENERIC_READ,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_FLAG_RANDOM_ACCESS, NULL);
+ assert(file_handle != INVALID_HANDLE_VALUE);
+ if (file_handle == INVALID_HANDLE_VALUE) {
+ _set_errno(GetLastError());
+ return NULL;
+ }
+
+ DWORD read_bytes;
+ BOOL res = ReadFile(file_handle, &header, sizeof(header), &read_bytes, NULL);
+ CloseHandle(file_handle);
+ assert(res && read_bytes == sizeof(header));
+ if (!res || read_bytes != sizeof(header)) {
+ _set_errno(GetLastError());
+ return NULL;
+ }
+#else // WIN32
+ int file_handle = open(path, O_RDONLY | O_BINARY, 0);
+ assert(file_handle >= 0);
+ if (file_handle < 0) {
+ return NULL;
+ }
+ const ssize_t read_bytes = read(file_handle, &header, sizeof(header));
+ close(file_handle);
+ assert(read_bytes != -1 && read_bytes == sizeof(header));
+ if (read_bytes == -1 || read_bytes != sizeof(header)) {
+ if (read_bytes != -1) {
+ _set_errno(EINVAL);
+ }
+ return NULL;
+ }
+#endif // WIN32
+
+ /* Lets see if this is an ELF file at all. */
+ if (memcmp(elf_hdr->e_ident, ELFMAG, SELFMAG) != 0) {
+ /* File is not an ELF file. */
+ _set_errno(ENOEXEC);
+ return NULL;
+ }
+
+ /* Lets check ELF's "bitness". */
+ assert(elf_hdr->ei_info.ei_class == ELFCLASS32 ||
+ elf_hdr->ei_info.ei_class == ELFCLASS64);
+ if (elf_hdr->ei_info.ei_class != ELFCLASS32 &&
+ elf_hdr->ei_info.ei_class != ELFCLASS64) {
+ /* Neither 32, or 64-bit ELF file. Something wrong here. */
+ _set_errno(EBADF);
+ return NULL;
+ }
+
+ /* Lets instantiate appropriate ElfFileImpl object for this ELF. */
+ if (elf_hdr->ei_info.ei_class == ELFCLASS32) {
+ ret = new ElfFileImpl<Elf32_Addr, Elf32_Off>;
+ } else {
+ ret = new ElfFileImpl<Elf64_Addr, Elf64_Off>;
+ }
+ assert(ret != NULL);
+ if (ret != NULL) {
+ if (!ret->initialize(elf_hdr, path)) {
+ delete ret;
+ ret = NULL;
+ }
+ } else {
+ _set_errno(ENOMEM);
+ }
+
+ return ret;
+}
+
+bool ElfFile::initialize(const Elf_CommonHdr* elf_hdr, const char* path) {
+ /* Must be created first! */
+ allocator_ = new ElfAllocator();
+ assert(allocator_ != NULL);
+ if (allocator_ == NULL) {
+ _set_errno(ENOMEM);
+ return false;
+ }
+
+ /* Copy file path. */
+ size_t path_len = strlen(path) + 1;
+ elf_file_path_ = new char[path_len];
+ assert(elf_file_path_ != NULL);
+ if (elf_file_path_ == NULL) {
+ _set_errno(ENOMEM);
+ return false;
+ }
+ memcpy(elf_file_path_, path, path_len);
+
+ /* Cache some basic ELF properties. */
+ is_ELF_64_ = elf_hdr->ei_info.ei_class == ELFCLASS64;
+ is_elf_big_endian_ = elf_hdr->ei_info.ei_data == ELFDATA2MSB;
+ same_endianness_ = is_elf_little_endian() == is_little_endian_cpu();
+ is_exec_ = elf_hdr->e_type == 2;
+
+ /* Reopen file for further reads and mappings. */
+#ifdef WIN32
+ elf_handle_ = CreateFile(elf_file_path_, GENERIC_READ,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_FLAG_RANDOM_ACCESS, NULL);
+ assert(elf_handle_ != INVALID_HANDLE_VALUE);
+ if (elf_handle_ == INVALID_HANDLE_VALUE) {
+ _set_errno(GetLastError());
+ return false;
+ }
+#else // WIN32
+ elf_handle_ = open(elf_file_path_, O_RDONLY | O_BINARY, 0);
+ assert(elf_handle_ >= 0);
+ if (elf_handle_ < 0) {
+ elf_handle_ = INVALID_ELF_FILE_HANDLE;
+ return false;
+ }
+#endif // WIN32
+
+ return true;
+}
+
+bool ElfFile::get_pc_address_info(Elf_Xword address,
+ Elf_AddressInfo* address_info) {
+ assert(address_info != NULL);
+ if (address_info == NULL) {
+ _set_errno(EINVAL);
+ return false;
+ }
+
+ /* Collect routine information for all CUs in this file. */
+ if (parse_compilation_units(&parse_rt_context) == -1) {
+ return false;
+ }
+
+ /* Iterate through the collected CUs looking for the one that
+ * contains the given address. */
+ address_info->inline_stack = NULL;
+ DwarfCU* cu = last_cu();
+ while (cu != NULL) {
+ /* Find a leaf DIE object in the current CU that contains the address. */
+ Dwarf_AddressInfo info;
+ info.die_obj = cu->get_leaf_die_for_address(address);
+ if (info.die_obj != NULL) {
+ /* Convert the address to a location inside source file. */
+ if (cu->get_pc_address_file_info(address, &info)) {
+ /* Copy location information to the returning structure. */
+ address_info->file_name = info.file_name;
+ address_info->dir_name = info.dir_name;
+ address_info->line_number = info.line_number;
+ } else {
+ address_info->file_name = NULL;
+ address_info->dir_name = NULL;
+ address_info->line_number = 0;
+ }
+
+ /* Lets see if the DIE represents a routine (rather than
+ * a lexical block, for instance). */
+ Dwarf_Tag tag = info.die_obj->get_tag();
+ while (!dwarf_tag_is_routine(tag)) {
+ /* This is not a routine DIE. Lets loop trhough the parents of that
+ * DIE looking for the first routine DIE. */
+ info.die_obj = info.die_obj->parent_die();
+ if (info.die_obj == NULL) {
+ /* Reached compilation unit DIE. Can't go any further. */
+ address_info->routine_name = "<unknown>";
+ return true;
+ }
+ tag = info.die_obj->get_tag();
+ }
+
+ /* Save name of the routine that contains the address. */
+ address_info->routine_name = info.die_obj->get_name();
+ if (address_info->routine_name == NULL) {
+ /* In some cases (minimum debugging info in the file) routine
+ * name may be not avaible. We, however, are obliged by API
+ * considerations to return something in this field. */
+ address_info->routine_name = "<unknown>";
+ }
+
+ /* Lets see if address belongs to an inlined routine. */
+ if (tag != DW_TAG_inlined_subroutine) {
+ address_info->inline_stack = NULL;
+ return true;
+ }
+
+ /*
+ * Address belongs to an inlined routine. Create inline stack.
+ */
+
+ /* Allocate inline stack array big enough to fit all parent entries. */
+ address_info->inline_stack =
+ new Elf_InlineInfo[info.die_obj->get_level() + 1];
+ assert(address_info->inline_stack != NULL);
+ if (address_info->inline_stack == NULL) {
+ _set_errno(ENOMEM);
+ return false;
+ }
+ memset(address_info->inline_stack, 0,
+ sizeof(Elf_InlineInfo) * (info.die_obj->get_level() + 1));
+
+ /* Reverse DIEs filling in inline stack entries for inline
+ * routine tags. */
+ int inl_index = 0;
+ do {
+ /* Save source file information. */
+ DIEAttrib file_desc;
+ if (info.die_obj->get_attrib(DW_AT_call_file, &file_desc)) {
+ const Dwarf_STMTL_FileDesc* desc =
+ cu->get_stmt_file_info(file_desc.value()->u32);
+ if (desc != NULL) {
+ address_info->inline_stack[inl_index].inlined_in_file =
+ desc->file_name;
+ address_info->inline_stack[inl_index].inlined_in_file_dir =
+ cu->get_stmt_dir_name(desc->get_dir_index());
+ }
+ }
+ if (address_info->inline_stack[inl_index].inlined_in_file == NULL) {
+ address_info->inline_stack[inl_index].inlined_in_file = "<unknown>";
+ address_info->inline_stack[inl_index].inlined_in_file_dir = NULL;
+ }
+
+ /* Save source line information. */
+ if (info.die_obj->get_attrib(DW_AT_call_line, &file_desc)) {
+ address_info->inline_stack[inl_index].inlined_at_line = file_desc.value()->u32;
+ }
+
+ /* Advance DIE to the parent routine, and save its name. */
+ info.die_obj = info.die_obj->parent_die();
+ assert(info.die_obj != NULL);
+ if (info.die_obj != NULL) {
+ tag = info.die_obj->get_tag();
+ while (!dwarf_tag_is_routine(tag)) {
+ info.die_obj = info.die_obj->parent_die();
+ if (info.die_obj == NULL) {
+ break;
+ }
+ tag = info.die_obj->get_tag();
+ }
+ if (info.die_obj != NULL) {
+ address_info->inline_stack[inl_index].routine_name =
+ info.die_obj->get_name();
+ }
+ }
+ if (address_info->inline_stack[inl_index].routine_name == NULL) {
+ address_info->inline_stack[inl_index].routine_name = "<unknown>";
+ }
+
+ /* Continue with the parent DIE. */
+ inl_index++;
+ } while (info.die_obj != NULL && tag == DW_TAG_inlined_subroutine);
+
+ return true;
+ }
+ cu = cu->prev_cu();
+ }
+
+ return false;
+}
+
+void ElfFile::free_pc_address_info(Elf_AddressInfo* address_info) const {
+ assert(address_info != NULL);
+ if (address_info != NULL && address_info->inline_stack != NULL) {
+ delete address_info->inline_stack;
+ address_info->inline_stack = NULL;
+ }
+}
+
+//=============================================================================
+// ElfFileImpl
+//=============================================================================
+
+template <typename Elf_Addr, typename Elf_Off>
+bool ElfFileImpl<Elf_Addr, Elf_Off>::initialize(const Elf_CommonHdr* elf_hdr,
+ const char* path) {
+ /* Must be called first! */
+ if (!ElfFile::initialize(elf_hdr, path)) {
+ return false;
+ }
+
+ /* Cache some header data, so later we can discard the header. */
+ const Elf_FHdr<Elf_Addr, Elf_Off>* header =
+ reinterpret_cast<const Elf_FHdr<Elf_Addr, Elf_Off>*>(elf_hdr);
+ sec_count_ = pull_val(header->e_shnum);
+ sec_entry_size_ = pull_val(header->e_shentsize);
+ fixed_base_address_ = pull_val(header->e_entry) & ~0xFFF;
+
+ /* Cache section table (must have one!) */
+ const Elf_Off sec_table_off = pull_val(header->e_shoff);
+ assert(sec_table_off != 0 && sec_count_ != 0);
+ if (sec_table_off == 0 || sec_count_ == 0) {
+ _set_errno(EBADF);
+ return false;
+ }
+ const size_t sec_table_size = sec_count_ * sec_entry_size_;
+ sec_table_ = new Elf_Byte[sec_table_size];
+ assert(sec_table_ != NULL);
+ if (sec_table_ == NULL) {
+ _set_errno(ENOMEM);
+ return false;
+ }
+#ifdef WIN32
+ LARGE_INTEGER convert;
+ convert.QuadPart = sec_table_off;
+ if ((SetFilePointer(elf_handle_, convert.LowPart,
+ &convert.HighPart,
+ FILE_BEGIN) == INVALID_SET_FILE_POINTER) &&
+ (GetLastError() != NO_ERROR)) {
+ _set_errno(GetLastError());
+ return false;
+ }
+ DWORD read_bytes;
+ BOOL res =
+ ReadFile(elf_handle_, sec_table_, sec_table_size, &read_bytes, NULL);
+ assert(res && read_bytes == sec_table_size);
+ if (!res || read_bytes != sec_table_size) {
+ _set_errno(GetLastError());
+ return false;
+ }
+#else // WIN32
+ ssize_t res = lseek(elf_handle_, sec_table_off, SEEK_SET);
+ assert(res != -1);
+ if (res == -1) {
+ return false;
+ }
+ res = read(elf_handle_, sec_table_, sec_table_size);
+ assert(res != -1 && res == sec_table_size);
+ if (res == -1 || res != sec_table_size) {
+ if (res != -1) {
+ _set_errno(EINVAL);
+ }
+ return false;
+ }
+#endif // WIN32
+
+ /* Map ELF's string section (must have one!). */
+ const Elf_Half str_sec_index = pull_val(header->e_shstrndx);
+ assert(str_sec_index != SHN_UNDEF);
+ if (str_sec_index == SHN_UNDEF) {
+ _set_errno(EBADF);
+ return false;
+ }
+ const Elf_SHdr<Elf_Addr, Elf_Off>* str_sec =
+ reinterpret_cast<const Elf_SHdr<Elf_Addr, Elf_Off>*>
+ (get_section_by_index(str_sec_index));
+ assert(str_sec != NULL);
+ if (str_sec == NULL) {
+ _set_errno(EBADF);
+ return false;
+ }
+ if (!string_section_.map(elf_handle_, pull_val(str_sec->sh_offset),
+ pull_val(str_sec->sh_size))) {
+ return false;
+ }
+
+ /* Lets determine DWARF format. According to the docs, DWARF is 64 bit, if
+ * first 4 bytes in the compilation unit header are set to 0xFFFFFFFF.
+ * .debug_info section of the ELF file begins with the first CU header. */
+ if (!map_section_by_name(".debug_info", &debug_info_)) {
+ _set_errno(EBADF);
+ return false;
+ }
+
+ /* Note that we don't care about endianness here, since 0xFFFFFFFF is an
+ * endianness-independent value, so we don't have to pull_val here. */
+ is_DWARF_64_ =
+ *reinterpret_cast<const Elf_Word*>(debug_info_.data()) == 0xFFFFFFFF;
+
+ return true;
+}
+
+template <typename Elf_Addr, typename Elf_Off>
+int ElfFileImpl<Elf_Addr, Elf_Off>::parse_compilation_units(
+ const DwarfParseContext* parse_context) {
+ /* Lets see if we already parsed the file. */
+ if (last_cu() != NULL) {
+ return cu_count_;
+ }
+
+ /* Cache sections required for this parsing. */
+ if (!map_section_by_name(".debug_abbrev", &debug_abbrev_) ||
+ !map_section_by_name(".debug_ranges", &debug_ranges_) ||
+ !map_section_by_name(".debug_line", &debug_line_) ||
+ !map_section_by_name(".debug_str", &debug_str_)) {
+ _set_errno(EBADF);
+ return false;
+ }
+
+ /* .debug_info section opens with the first CU header. */
+ const void* next_cu = debug_info_.data();
+
+ /* Iterate through CUs until we reached the end of .debug_info section, or
+ * advanced to a CU with zero size, indicating the end of CU list for this
+ * file. */
+ while (is_valid_cu(next_cu)) {
+ /* Instatiate CU, depending on DWARF "bitness". */
+ DwarfCU* cu = DwarfCU::create_instance(this, next_cu);
+ if (cu == NULL) {
+ _set_errno(ENOMEM);
+ return -1;
+ }
+
+ if (cu->parse(parse_context, &next_cu)) {
+ cu->set_prev_cu(last_cu_);
+ last_cu_ = cu;
+ cu_count_++;
+ } else {
+ delete cu;
+ return -1;
+ }
+ };
+
+ return cu_count_;
+}
+
+template <typename Elf_Addr, typename Elf_Off>
+bool ElfFileImpl<Elf_Addr, Elf_Off>::get_section_info_by_name(const char* name,
+ Elf_Off* offset,
+ Elf_Word* size) {
+ const Elf_SHdr<Elf_Addr, Elf_Off>* cur_section =
+ reinterpret_cast<const Elf_SHdr<Elf_Addr, Elf_Off>*>(sec_table_);
+
+ for (Elf_Half sec = 0; sec < sec_count_; sec++) {
+ const char* sec_name = get_str_sec_str(pull_val(cur_section->sh_name));
+ if (sec_name != NULL && strcmp(name, sec_name) == 0) {
+ *offset = pull_val(cur_section->sh_offset);
+ *size = pull_val(cur_section->sh_size);
+ return true;
+ }
+ cur_section = reinterpret_cast<const Elf_SHdr<Elf_Addr, Elf_Off>*>
+ (INC_CPTR(cur_section, sec_entry_size_));
+ }
+ _set_errno(EINVAL);
+ return false;
+}
+
+template <typename Elf_Addr, typename Elf_Off>
+bool ElfFileImpl<Elf_Addr, Elf_Off>::map_section_by_name(
+ const char* name,
+ ElfMappedSection* section) {
+ if (section->is_mapped()) {
+ return true;
+ }
+
+ Elf_Off offset;
+ Elf_Word size;
+ if (!get_section_info_by_name(name, &offset, &size)) {
+ return false;
+ }
+
+ return section->map(elf_handle_, offset, size);
+}
diff --git a/elff/elf_file.h b/elff/elf_file.h new file mode 100644 index 0000000..6c57bde --- /dev/null +++ b/elff/elf_file.h @@ -0,0 +1,621 @@ +/* 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 declaration of ElfFile classes that encapsulate an ELF file.
+ */
+
+#ifndef ELFF_ELF_FILE_H_
+#define ELFF_ELF_FILE_H_
+
+#include "dwarf_die.h"
+#include "elf_mapped_section.h"
+#include "elff_api.h"
+
+/* Encapsulates architecture-independent functionality of an ELF file.
+ *
+ * This class is a base class for templated ElfFileImpl. This class implements
+ * functionality around an ELF file that is independent from particulars of the
+ * ELF's CPU architectire, while ElfFileImpl handles all particulars of CPU
+ * architecture (namely, 32 or 64-bit), for which ELF file has been built.
+ *
+ * NOTE: This class operates on ELF sections that have been mapped to memory.
+ *
+ */
+class ElfFile {
+ public:
+ /* Constructs ElfFile instance. */
+ ElfFile();
+
+ /* Destructs ElfFile instance. */
+ virtual ~ElfFile();
+
+ /* Creates ElfFileImpl instance, depending on ELF file CPU architecture.
+ * This method will collect initial information about requested ELF file,
+ * and will instantiate appropriate ElfFileImpl class object for it.
+ * Param:
+ * path - Full path to the ELF file.
+ * Return:
+ * Initialized ElfFileImpl instance, typecasted back to ElfFile object on
+ * success, or NULL on failure, with errno providing extended error
+ * information.
+ */
+ static ElfFile* Create(const char* path);
+
+ /* Checks if ELF file is a 64, or 32-bit ELF file. */
+ bool is_ELF_64() const {
+ return is_ELF_64_;
+ }
+ bool is_ELF_32() const {
+ return !is_ELF_64_;
+ }
+
+ /* Checks if ELF file data format is big, or little-endian. */
+ bool is_elf_big_endian() const {
+ return is_elf_big_endian_;
+ }
+ bool is_elf_little_endian() const {
+ return !is_elf_big_endian_;
+ }
+
+ /* Checks whether or not endianness of CPU this library is built for matches
+ * endianness of the ELF file that is represented with this instance. */
+ bool same_endianness() const {
+ return same_endianness_;
+ }
+
+ /* Checks if format of DWARF data in this file is 64, or 32-bit. */
+ bool is_DWARF_64() const {
+ return is_DWARF_64_;
+ }
+ bool is_DWARF_32() const {
+ return !is_DWARF_64_;
+ }
+
+ /* Gets DWARF objects allocator for this instance. */
+ class ElfAllocator* allocator() const {
+ return allocator_;
+ }
+
+ /* Gets head of compilation unit list, collected during parsing of this file.
+ * NOTE: list of collected compilation units returned from this method is
+ * in reverse order relatively to the order CUs have been added to the list
+ * during ELF file parsing.
+ */
+ class DwarfCU* last_cu() const {
+ return last_cu_;
+ }
+
+ /* Gets number of compilation units, collected during parsing of
+ * this ELF file with parse_compilation_units() method.
+ */
+ int cu_count() const {
+ return cu_count_;
+ }
+
+ /* Gets executable file flag */
+ bool is_exec() const {
+ return is_exec_;
+ }
+
+ protected:
+ /* Initializes ElfFile instance. This method is called from Create method of
+ * this class after appropriate ElfFileImpl instance has been created. Note,
+ * that Create() method will validate that requested file is an ELF file,
+ * prior to instantiating of an ElfFileImpl object, and calling this method.
+ * Param:
+ * elf_hdr - Address of the common ELF file header.
+ * path - See Create().
+ * Return:
+ * true on success, or false on failure, with errno containing extended
+ * error information.
+ */
+ virtual bool initialize(const Elf_CommonHdr* elf_hdr, const char* path);
+
+/*=============================================================================
+ * Endianness helper methods.
+ * Since endianness of ELF file may differ from the endianness of the CPU this
+ * library runs on, every time a value is required from a section of the ELF
+ * file, it must be first pulled out of that section to a local variable, and
+ * then used from that local variable. While value is pulled from ELF file
+ * section, it must be converted accordingly to the endianness of the CPU and
+ * ELF file. Routines bellow provide such functionality.
+=============================================================================*/
+
+ public:
+ /* Pulls one byte value from ELF file. Note that for one byte we don't need
+ * to do any endianness conversion, and these two methods are provided purely
+ * for completness of the API.
+ * Param:
+ * val - References value inside ELF file buffer to pull data from.
+ * Return
+ * Pulled value with endianness appropriate for the CPU this library is
+ * running on.
+ */
+ uint8_t pull_val(const uint8_t* val) const {
+ return *val;
+ }
+ uint8_t pull_val(const uint8_t& val) const {
+ return val;
+ }
+ int8_t pull_val(const int8_t* val) const {
+ return *val;
+ }
+ int8_t pull_val(const int8_t& val) const {
+ return val;
+ }
+
+ /* Pulls two byte value from ELF file.
+ * Param:
+ * val - References value inside ELF file buffer to pull data from.
+ * Return
+ * Pulled value with endianness appropriate for the CPU this library is
+ * running on.
+ */
+ uint16_t pull_val(const uint16_t* val) const {
+ if (same_endianness()) {
+ return *val;
+ }
+ if (is_elf_big_endian()) {
+ return (uint16_t)get_byte(val, 0) << 8 | get_byte(val, 1);
+ } else {
+ return (uint16_t)get_byte(val, 1) << 8 | get_byte(val, 0);
+ }
+ }
+ uint16_t pull_val(const uint16_t& val) const {
+ return same_endianness() ? val : pull_val(&val);
+ }
+ int16_t pull_val(const int16_t* val) const {
+ return static_cast<int16_t>
+ (pull_val(reinterpret_cast<const uint16_t*>(val)));
+ }
+ int16_t pull_val(const int16_t& val) const {
+ return static_cast<int16_t>
+ (pull_val(reinterpret_cast<const uint16_t&>(val)));
+ }
+
+ /* Pulls four byte value from ELF file.
+ * Param:
+ * val - References value inside ELF file buffer to pull data from.
+ * Return
+ * Pulled value with endianness appropriate for the CPU this library is
+ * running on.
+ */
+ uint32_t pull_val(const uint32_t* val) const {
+ if (same_endianness()) {
+ return *val;
+ }
+ if (is_elf_big_endian()) {
+ return (uint32_t)get_byte(val, 0) << 24 |
+ (uint32_t)get_byte(val, 1) << 16 |
+ (uint32_t)get_byte(val, 2) << 8 |
+ (uint32_t)get_byte(val, 3);
+ } else {
+ return (uint32_t)get_byte(val, 3) << 24 |
+ (uint32_t)get_byte(val, 2) << 16 |
+ (uint32_t)get_byte(val, 1) << 8 |
+ (uint32_t)get_byte(val, 0);
+ }
+ }
+ uint32_t pull_val(const uint32_t& val) const {
+ return same_endianness() ? val : pull_val(&val);
+ }
+ int32_t pull_val(const int32_t* val) const {
+ return static_cast<int32_t>
+ (pull_val(reinterpret_cast<const uint32_t*>(val)));
+ }
+ int32_t pull_val(const int32_t& val) const {
+ return static_cast<int32_t>
+ (pull_val(reinterpret_cast<const uint32_t&>(val)));
+ }
+
+ /* Pulls eight byte value from ELF file.
+ * Param:
+ * val - References value inside ELF file buffer to pull data from.
+ * Return
+ * Pulled value with endianness appropriate for the CPU this library is
+ * running on.
+ */
+ uint64_t pull_val(const uint64_t* val) const {
+ if (same_endianness()) {
+ return *val;
+ }
+ if (is_elf_big_endian()) {
+ return (uint64_t)get_byte(val, 0) << 56 |
+ (uint64_t)get_byte(val, 1) << 48 |
+ (uint64_t)get_byte(val, 2) << 40 |
+ (uint64_t)get_byte(val, 3) << 32 |
+ (uint64_t)get_byte(val, 4) << 24 |
+ (uint64_t)get_byte(val, 5) << 16 |
+ (uint64_t)get_byte(val, 6) << 8 |
+ (uint64_t)get_byte(val, 7);
+ } else {
+ return (uint64_t)get_byte(val, 7) << 56 |
+ (uint64_t)get_byte(val, 6) << 48 |
+ (uint64_t)get_byte(val, 5) << 40 |
+ (uint64_t)get_byte(val, 4) << 32 |
+ (uint64_t)get_byte(val, 3) << 24 |
+ (uint64_t)get_byte(val, 2) << 16 |
+ (uint64_t)get_byte(val, 1) << 8 |
+ (uint64_t)get_byte(val, 0);
+ }
+ }
+ uint64_t pull_val(const uint64_t& val) const {
+ return same_endianness() ? val : pull_val(&val);
+ }
+ int64_t pull_val(const int64_t* val) const {
+ return static_cast<int64_t>
+ (pull_val(reinterpret_cast<const uint64_t*>(val)));
+ }
+ int64_t pull_val(const int64_t& val) const {
+ return static_cast<int64_t>
+ (pull_val(reinterpret_cast<const uint64_t&>(val)));
+ }
+
+//=============================================================================
+// ELF file section management.
+//=============================================================================
+
+ public:
+ /* Gets a string contained in ELF's string section by index.
+ * Param:
+ * index - String index (byte offset) in the ELF's string section.
+ * Return:
+ * Pointer to the requested string, or NULL if string index exceeds ELF's
+ * string section size.
+ * NOTE: pointer returned from this method points to a mapped section of
+ * ELF file.
+ */
+ const char* get_str_sec_str(Elf_Xword index) const {
+ assert(string_section_.is_mapped() && index < string_section_.size());
+ if (string_section_.is_mapped() && index < string_section_.size()) {
+ return INC_CPTR_T(char, string_section_.data(), index);
+ } else {
+ _set_errno(EINVAL);
+ return NULL;
+ }
+ }
+
+ /* Gets a string contained in ELF's debug string section (.debug_str)
+ * by index.
+ * Param:
+ * index - String index (byte offset) in the ELF's debug string section.
+ * Return:
+ * Pointer to the requested string, or NULL if string index exceeds ELF's
+ * debug string section size.
+ * NOTE: pointer returned from this method points to a mapped section of
+ * ELF file.
+ */
+ const char* get_debug_str(Elf_Xword index) const {
+ assert(debug_str_.is_mapped() && index < debug_str_.size());
+ if (debug_str_.is_mapped() && index < debug_str_.size()) {
+ return INC_CPTR_T(char, debug_str_.data(), index);
+ } else {
+ _set_errno(EINVAL);
+ return NULL;
+ }
+ }
+
+ protected:
+ /* Gets pointer to a section header, given section index within ELF's
+ * section table.
+ * Param:
+ * index - Section index within ELF's section table.
+ * Return:
+ * Pointer to a section header (ElfXX_SHdr flavor, depending on ELF's CPU
+ * architecture) on success, or NULL if section index exceeds number of
+ * sections for this ELF file.
+ */
+ const void* get_section_by_index(Elf_Half index) const {
+ assert(index < sec_count_);
+ if (index < sec_count_) {
+ return INC_CPTR(sec_table_, static_cast<size_t>(index) * sec_entry_size_);
+ } else {
+ _set_errno(EINVAL);
+ return NULL;
+ }
+ }
+
+//=============================================================================
+// DWARF management.
+//=============================================================================
+
+ protected:
+ /* Parses DWARF, and buids a list of compilation units for this ELF file.
+ * Compilation unit, collected with this methods are linked together in a
+ * list, head of which is available via last_cu() method of this class.
+ * NOTE: CUs in the list returned via last_cu() method are in reverse order
+ * relatively to the order in which CUs are stored in .debug_info section.
+ * This is ELF and DWARF data format - dependent method.
+ * Param:
+ * parse_context - Parsing context that defines which tags, and which
+ * properties for which tag should be collected during parsing. NULL
+ * passed in this parameter indicates that all properties for all tags
+ * should be collected.
+ * Return:
+ * Number of compilation units, collected in this method on success,
+ * or -1 on failure.
+ */
+ virtual int parse_compilation_units(const DwarfParseContext* parse_context) = 0;
+
+ public:
+ /* Gets PC address information.
+ * Param:
+ * address - PC address to get information for. The address must be relative
+ * to the beginning of ELF file represented by this class.
+ * address_info - Upon success contains information about routine(s) that
+ * contain the given address.
+ * Return:
+ * true if routine(s) containing has been found and its information has been
+ * saved into address_info, or false if no appropriate routine for that
+ * address has been found, or there was a memory error when collecting
+ * routine(s) information. In case of failure, errno contains extended error
+ * information.
+ */
+ bool get_pc_address_info(Elf_Xword address, Elf_AddressInfo* address_info);
+
+ /* Frees resources aqcuired for address information in successful call to
+ * get_pc_address_info().
+ * Param:
+ * address_info - Address information structure, initialized in successful
+ * call to get_pc_address_info() routine.
+ */
+ void free_pc_address_info(Elf_AddressInfo* address_info) const;
+
+ /* Gets beginning of the .debug_info section data.
+ * Return:
+ * Beginning of the .debug_info section data.
+ * NOTE: pointer returned from this method points to a mapped section of
+ * ELF file.
+ */
+ const void* get_debug_info_data() const {
+ return debug_info_.data();
+ }
+
+ /* Gets beginning of the .debug_abbrev section data.
+ * Return:
+ * Beginning of the .debug_abbrev section data.
+ * NOTE: pointer returned from this method points to a mapped section of
+ * ELF file.
+ */
+ const void* get_debug_abbrev_data() const {
+ return debug_abbrev_.data();
+ }
+
+ /* Gets beginning of the .debug_ranges section data.
+ * Return:
+ * Beginning of the .debug_ranges section data.
+ * NOTE: pointer returned from this method points to a mapped section of
+ * ELF file.
+ */
+ const void* get_debug_ranges_data() const {
+ return debug_ranges_.data();
+ }
+
+ /* Gets beginning of the .debug_line section data.
+ * Return:
+ * Beginning of the .debug_line section data.
+ * NOTE: pointer returned from this method points to a mapped section of
+ * ELF file.
+ */
+ const void* get_debug_line_data() const {
+ return debug_line_.data();
+ }
+
+ /* Checks, if given address range is contained in the mapped .debug_info
+ * section of this file.
+ * Param:
+ * ptr - Starting address of the range.
+ * size - Range size in bytes.
+ * Return:
+ * true if given address range is contained in the mapped .debug_info
+ * section of this file, or false if any part of the range doesn't belong
+ * to that section.
+ */
+ bool is_valid_die_ptr(const void* ptr, size_t size) const {
+ return debug_info_.is_contained(ptr, size);
+ }
+
+ /* Checks, if given address range is contained in the mapped .debug_abbrev
+ * section of this file.
+ * Param:
+ * ptr - Starting address of the range.
+ * size - Range size in bytes.
+ * Return:
+ * true if given address range is contained in the mapped .debug_abbrev
+ * section of this file, or false if any part of the range doesn't belong
+ * to that section.
+ */
+ bool is_valid_abbr_ptr(const void* ptr, size_t size) const {
+ return debug_abbrev_.is_contained(ptr, size);
+ }
+
+ /* Checks if given pointer addresses a valid compilation unit header in the
+ * mapped .debug_info section of the ELF file.
+ * Param:
+ * cu_header - Pointer to a compilation unit header to check.
+ * Return
+ * true, if given pointer addresses a valid compilation unit header, or
+ * false, if it's not. A valid CU header must be fully conained inside
+ * .debug_info section of the ELF file, and its size must not be zero.
+ */
+ bool is_valid_cu(const void* cu_header) const {
+ if (is_DWARF_64()) {
+ return is_valid_die_ptr(cu_header, sizeof(Dwarf64_CUHdr)) &&
+ reinterpret_cast<const Dwarf64_CUHdr*>(cu_header)->size_hdr.size != 0;
+ } else {
+ return is_valid_die_ptr(cu_header, sizeof(Dwarf32_CUHdr)) &&
+ reinterpret_cast<const Dwarf32_CUHdr*>(cu_header)->size_hdr.size != 0;
+ }
+ }
+
+ /* Gets range's low and high pc for the given range reference in the mapped
+ * .debug_ranges section of an ELF file.
+ * Template param:
+ * AddrType - Defines pointer type for the CU the range belongs to. CU's
+ * pointer type can be defined independently from ELF and DWARF types,
+ * and is encoded in address_size field of the CU header in .debug_info
+ * section of ELF file.
+ * Param:
+ * offset - Byte offset within .debug_ranges section of the range record.
+ * low - Upon successful return contains value for range's low pc.
+ * high - Upon successful return contains value for range's high pc.
+ * Return:
+ * true on success, or false, if requested record is not fully contained
+ * in the .debug_ranges section.
+ */
+ template<typename AddrType>
+ bool get_range(Elf_Word offset, AddrType* low, AddrType* high) {
+ const AddrType* ptr = INC_CPTR_T(AddrType, debug_ranges_.data(), offset);
+ assert(debug_ranges_.is_contained(ptr, sizeof(AddrType) * 2));
+ if (!debug_ranges_.is_contained(ptr, sizeof(AddrType) * 2)) {
+ _set_errno(EINVAL);
+ return false;
+ }
+ *low = pull_val(ptr);
+ *high = pull_val(ptr + 1);
+ return true;
+ }
+
+ protected:
+ /* Mapped ELF string section. */
+ ElfMappedSection string_section_;
+
+ /* Mapped .debug_info section. */
+ ElfMappedSection debug_info_;
+
+ /* Mapped .debug_abbrev section. */
+ ElfMappedSection debug_abbrev_;
+
+ /* Mapped .debug_str section. */
+ ElfMappedSection debug_str_;
+
+ /* Mapped .debug_line section. */
+ ElfMappedSection debug_line_;
+
+ /* Mapped .debug_ranges section. */
+ ElfMappedSection debug_ranges_;
+
+ /* Base address of the loaded module (if fixed), or 0 if module doesn't get
+ * loaded at fixed address. */
+ Elf_Xword fixed_base_address_;
+
+ /* Handle to the ELF file represented with this instance. */
+ ELF_FILE_HANDLE elf_handle_;
+
+ /* Path to the ELF file represented with this instance. */
+ char* elf_file_path_;
+
+ /* DWARF objects allocator for this instance. */
+ class ElfAllocator* allocator_;
+
+ /* Beginning of the cached ELF's section table. */
+ void* sec_table_;
+
+ /* Number of sections in the ELF file wrapped by this instance. */
+ Elf_Half sec_count_;
+
+ /* Byte size of an entry in the section table. */
+ Elf_Half sec_entry_size_;
+
+ /* Head of compilation unit list, collected during the parsing. */
+ class DwarfCU* last_cu_;
+
+ /* Number of compilation units in last_cu_ list. */
+ int cu_count_;
+
+ /* Flags ELF's CPU architecture: 64 (true), or 32 bits (false). */
+ bool is_ELF_64_;
+
+ /* Flags endianness of the processed ELF file. true indicates that ELF file
+ * data is stored in big-endian form, false indicates that ELF file data is
+ * stored in big-endian form.
+ */
+ bool is_elf_big_endian_;
+
+ /* Flags whether or not endianness of CPU this library is built for matches
+ * endianness of the ELF file that is represented with this instance.
+ */
+ bool same_endianness_;
+
+ /* Flags DWARF format: 64, or 32 bits. DWARF format is determined by looking
+ * at the first 4 bytes of .debug_info section (which is the beginning of the
+ * first compilation unit header). If first 4 bytes contain 0xFFFFFFFF, the
+ * DWARF is 64 bit. Otherwise, DWARF is 32 bit. */
+ bool is_DWARF_64_;
+
+ /* Flags executable file. If this member is 1, ELF file represented with this
+ * instance is an executable. If this member is 0, file is a shared library.
+ */
+ bool is_exec_;
+};
+
+/* Encapsulates architecture-dependent functionality of an ELF file.
+ * Template param:
+ * Elf_Addr - type for an address field in ELF file. Must be:
+ * - Elf32_Addr for 32-bit CPU, or
+ * - Elf64_Addr for 64-bit CPU.
+ * Elf_Off - type for an offset field in ELF file. Must be:
+ * - Elf64_Off for 32-bit CPU, or
+ * - Elf64_Off for 64-bit CPU.
+ */
+template <typename Elf_Addr, typename Elf_Off>
+class ElfFileImpl : protected ElfFile {
+/* Instance of this class must be instantiated from
+ * ElfFile::Create() method only. */
+friend class ElfFile;
+ protected:
+ /* Constructs ElfFileImpl instance. */
+ ElfFileImpl() {
+ };
+
+ /* Destructs ElfFileImpl instance. */
+ ~ElfFileImpl() {
+ }
+
+ protected:
+ /* Initializes instance. This is an override of the base class method.
+ * See ElfFile::initialize().
+ */
+ bool initialize(const Elf_CommonHdr* elf_hdr, const char* path);
+
+ /* Parses DWARF, and buids list of compilation units for this ELF file.
+ * This is an implementation of the base class' abstract method.
+ * See ElfFile::parse_compilation_units().
+ */
+ virtual int parse_compilation_units(const DwarfParseContext* parse_context);
+
+ /* Gets section information by section name.
+ * Param:
+ * name - Name of the section to get information for.
+ * offset - Upon success contains offset of the section data in ELF file.
+ * size - Upon success contains size of the section data in ELF file.
+ * Return:
+ * true on sucess, or false if section with such name doesn't exist in
+ * this ELF file.
+ */
+ bool get_section_info_by_name(const char* name,
+ Elf_Off* offset,
+ Elf_Word* size);
+
+ /* Maps section by its name.
+ * Param:
+ * name - Name of the section to map.
+ * section - Upon success contains section's mapping information.
+ * Return:
+ * true on sucess, or false if section with such name doesn't exist in
+ * this ELF file, or mapping has failed.
+ */
+ bool map_section_by_name(const char* name, ElfMappedSection* section);
+};
+
+#endif // ELFF_ELF_FILE_H_
diff --git a/elff/elf_mapped_section.cc b/elff/elf_mapped_section.cc new file mode 100644 index 0000000..46fc6b6 --- /dev/null +++ b/elff/elf_mapped_section.cc @@ -0,0 +1,95 @@ +/* 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 a class ElfMappedSection, that encapsulates
+ * a section of an ELF file, mapped to memory.
+ */
+
+#include "elf_defs.h"
+#include "elf_mapped_section.h"
+
+ElfMappedSection::ElfMappedSection()
+ : mapped_at_(NULL),
+ data_(NULL),
+ size_(0) {
+}
+
+ElfMappedSection::~ElfMappedSection() {
+ if (mapped_at_ != NULL) {
+#ifdef WIN32
+ UnmapViewOfFile(mapped_at_);
+#else // WIN32
+ munmap(mapped_at_, diff_ptr(mapped_at_, data_) + size_);
+#endif // WIN32
+ }
+}
+
+bool ElfMappedSection::map(ELF_FILE_HANDLE handle,
+ Elf_Xword offset,
+ Elf_Word size) {
+ /* Get the mask for mapping offset alignment. */
+#ifdef WIN32
+ SYSTEM_INFO sys_info;
+ GetSystemInfo(&sys_info);
+ const Elf_Xword align_mask = sys_info.dwAllocationGranularity - 1;
+#else // WIN32
+ const Elf_Xword align_mask = getpagesize() - 1;
+#endif // WIN32
+
+ /* Adjust mapping offset and mapping size accordingly to
+ * the mapping alignment requirements. */
+ const Elf_Xword map_offset = offset & ~align_mask;
+ const Elf_Word map_size = static_cast<Elf_Word>(offset - map_offset + size);
+
+ /* Make sure mapping size doesn't exceed 4G: may happen on 64-bit ELFs, if
+ * section size is close to 4G, while section offset is badly misaligned. */
+ assert(map_size >= size);
+ if (map_size < size) {
+ _set_errno(EFBIG);
+ return false;
+ }
+
+ /* Map the section. */
+#ifdef WIN32
+ LARGE_INTEGER converter;
+ converter.QuadPart = map_offset + map_size;
+ HANDLE map_handle = CreateFileMapping(handle, NULL, PAGE_READONLY,
+ converter.HighPart, converter.LowPart,
+ NULL);
+ assert(map_handle != NULL);
+ if (map_handle != NULL) {
+ converter.QuadPart = map_offset;
+ mapped_at_ = MapViewOfFile(map_handle, FILE_MAP_READ, converter.HighPart,
+ converter.LowPart, map_size);
+ assert(mapped_at_ != NULL);
+ /* Memory mapping (if successful) will hold extra references to the
+ * mapping, so we can close it right after we mapped file view. */
+ CloseHandle(map_handle);
+ }
+ if (mapped_at_ == NULL) {
+ _set_errno(GetLastError());
+ return false;
+ }
+#else // WIN32
+ mapped_at_ = mmap(0, map_size, PROT_READ, MAP_SHARED, handle, map_offset);
+ assert(mapped_at_ != MAP_FAILED);
+ if (mapped_at_ == MAP_FAILED) {
+ return false;
+ }
+#endif // WIN32
+
+ data_ = INC_CPTR(mapped_at_, offset - map_offset);
+ size_ = size;
+
+ return true;
+}
diff --git a/elff/elf_mapped_section.h b/elff/elf_mapped_section.h new file mode 100644 index 0000000..2f3ca56 --- /dev/null +++ b/elff/elf_mapped_section.h @@ -0,0 +1,88 @@ +/* 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 declaration of a class ElfMappedSection, that encapsulates
+ * a section of an ELF file, mapped to memory.
+ */
+
+#ifndef ELFF_ELF_MAPPED_SECTION_H_
+#define ELFF_ELF_MAPPED_SECTION_H_
+
+#include "elf_defs.h"
+
+/* Encapsulates a section of an ELF file, mapped to memory. */
+class ElfMappedSection {
+ public:
+ /* Constructs ElfMappedSection instance. */
+ ElfMappedSection();
+
+ /* Destructs ElfMappedSection instance. */
+ ~ElfMappedSection();
+
+ /* Maps ELF file section to memory.
+ * Param:
+ * handle - Handle to an opened ELF file.
+ * offset - Offset of the beginning of the section data in ELF file.
+ * NOTE: we explicitly use 64-bit type for this parameter, since we may
+ * still allow 32-bit library to process 64 bits ELF/DWARF formats. We
+ * really only care about section size being small enough to fit in 32
+ * bits value in this case (which seems to be always true for ELF files,
+ * as section size is encoded with 32-bit value even in 64-bit ELF file).
+ * size - Section byte size in ELF file.
+ * Return:
+ * true on success, or false on failure, with errno providing extended
+ * error information.
+ * NOTE: if section has already been mapped, this method immediately
+ * returns with success.
+ */
+ bool map(ELF_FILE_HANDLE handle, Elf_Xword offset, Elf_Word size);
+
+ /* Checks if section has been mapped. */
+ bool is_mapped() const {
+ return mapped_at_ != NULL;
+ }
+
+ /* Gets address of the beginning of the mapped section. */
+ const void* data() const {
+ assert(is_mapped());
+ return data_;
+ }
+
+ /* Gets section size. */
+ Elf_Word size() const {
+ assert(is_mapped());
+ return size_;
+ }
+
+ /* Checks if an address range is fully contained in this section. */
+ bool is_contained(const void* ptr, size_t rsize) const {
+ assert(is_mapped());
+ return is_mapped() && is_in_section(ptr, rsize, data(), size());
+ }
+
+ protected:
+ /* Beginning of the memory mapping, containing the section.
+ * NOTE: due to page alignment requirements of the mapping API, mapping
+ * address may differ from the address where the actual section data
+ * starts inside that mapping.
+ */
+ void* mapped_at_;
+
+ /* Address of the beginning of the mapped section. */
+ const void* data_;
+
+ /* Section size. */
+ Elf_Word size_;
+};
+
+#endif // ELFF_ELF_MAPPED_SECTION_H_
diff --git a/elff/elff-common.h b/elff/elff-common.h new file mode 100644 index 0000000..922de10 --- /dev/null +++ b/elff/elff-common.h @@ -0,0 +1,59 @@ +/* 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.
+*/
+
+/*
+ * Includes common headers for the ELFF library.
+ */
+
+#ifndef ELFF_ELFF_COMMON_H_
+#define ELFF_ELFF_COMMON_H_
+
+#include "stddef.h"
+#include "sys/types.h"
+#include "assert.h"
+#include "memory.h"
+#include "errno.h"
+#ifdef WIN32
+#include "Windows.h"
+#else // WIN32
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif // WIN32
+
+static inline void _set_errno(uint32_t err) {
+ errno = err;
+}
+
+/* Main operator new. We overwrite it to redirect memory
+ * allocations to qemu_malloc, instead of malloc. */
+inline void* operator new(size_t size) {
+ return qemu_malloc(size);
+}
+
+/* Main operator delete. We overwrite it to redirect memory
+ * deallocation to qemu_free, instead of free. */
+inline void operator delete(void* p) {
+ if (p != NULL) {
+ qemu_free(p);
+ }
+}
+
+/* Main operator delete for arrays. We overwrite it to redirect
+ * memory deallocation to qemu_free, instead of free. */
+inline void operator delete[](void* p) {
+ if (p != NULL) {
+ qemu_free(p);
+ }
+}
+
+#endif // ELFF_ELFF_COMMON_H_
diff --git a/elff/elff_api.cc b/elff/elff_api.cc new file mode 100644 index 0000000..46b2ad1 --- /dev/null +++ b/elff/elff_api.cc @@ -0,0 +1,84 @@ +/* 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 encapsulte an API for parsing
+ * an ELF file containing debugging information in DWARF format.
+ */
+
+#include "elff_api.h"
+#include "elf_file.h"
+#include "dwarf_defs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ELFF_HANDLE
+elff_init(const char* elf_file_path)
+{
+ ElfFile* elf_file = ElfFile::Create(elf_file_path);
+ return reinterpret_cast<ELFF_HANDLE>(elf_file);
+}
+
+void
+elff_close(ELFF_HANDLE handle)
+{
+ if (handle != NULL) {
+ delete reinterpret_cast<ElfFile*>(handle);
+ }
+}
+
+int
+elff_is_exec(ELFF_HANDLE handle)
+{
+ assert(handle != NULL);
+ if (handle == NULL) {
+ _set_errno(EINVAL);
+ return -1;
+ }
+ return reinterpret_cast<ElfFile*>(handle)->is_exec();
+}
+
+int
+elff_get_pc_address_info(ELFF_HANDLE handle,
+ uint64_t address,
+ Elf_AddressInfo* address_info)
+{
+ assert(handle != NULL && address_info != NULL);
+ if (handle == NULL || address_info == NULL) {
+ _set_errno(EINVAL);
+ return -1;
+ }
+
+ if (reinterpret_cast<ElfFile*>(handle)->get_pc_address_info(address,
+ address_info)) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+void
+elff_free_pc_address_info(ELFF_HANDLE handle, Elf_AddressInfo* address_info)
+{
+ assert(handle != NULL && address_info != NULL);
+ if (handle == NULL || address_info == NULL) {
+ return;
+ }
+ reinterpret_cast<ElfFile*>(handle)->free_pc_address_info(address_info);
+}
+
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+
diff --git a/elff/elff_api.h b/elff/elff_api.h new file mode 100644 index 0000000..7b02746 --- /dev/null +++ b/elff/elff_api.h @@ -0,0 +1,173 @@ +/* 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 declaration of types, strctures, routines, etc. that encapsulte
+ * an API for parsing an ELF file containing debugging information in DWARF
+ * format.
+ */
+
+#ifndef ELFF_API_H_
+#define ELFF_API_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "qemu-common.h"
+
+/* Defines type for a handle used in ELFF API. */
+typedef void* ELFF_HANDLE;
+
+/* Defines an entry for 'inline_stack' array in Elf_AddressInfo structure.
+ * Each entry in the array represents a routine, where routine represented
+ * with the previous array entry has been inlined. First element in the array
+ * (at index 0) represents information for the inlined routine, referenced by
+ * Elf_AddressInfo structure itself. If name for a routine was not available
+ * (DW_AT_name attribute was missing), routine name is set to "<unknown>".
+ * Last entry in the array has all its fields set to zero. It's sufficient
+ * just to check for routine_name field of this structure to be NULL to detect
+ * last entry in the array.
+ */
+typedef struct Elf_InlineInfo {
+ /* Name of the routine where previous routine is inlined.
+ * This field can never be NULL, except for the last array entry.
+ */
+ const char* routine_name;
+
+ /* Source file name where routine is inlined.
+ * This field can be NULL, if it was not possible to obtain information
+ * about source file location for the routine. If this field is NULL, content
+ * of inlined_in_file_dir and inlined_at_line fields is undefined and should
+ * be ignored. */
+ const char* inlined_in_file;
+
+ /* Source file directory where routine is inlined.
+ * If inlined_in_file field contains NULL, content of this field is undefined
+ * and should be ignored. */
+ const char* inlined_in_file_dir;
+
+ /* Source file line number where routine is inlined.
+ * If inlined_in_file field contains NULL, content of this field is undefined
+ * and should be ignored. */
+ uint32_t inlined_at_line;
+} Elf_InlineInfo;
+
+/* Checks if an entry is the last entry in the array.
+ * Return:
+ * Boolean: 1 if this is last entry, or zero otherwise.
+ */
+static inline int
+elfinlineinfo_is_last_entry(const Elf_InlineInfo* info) {
+ return info->routine_name == 0;
+}
+
+/* PC address information descriptor.
+ * This descriptor contains as much information about a PC address as it was
+ * possible to collect from an ELF file. */
+typedef struct Elf_AddressInfo {
+ /* Name of the routine containing the address. If name of the routine
+ * was not available (DW_AT_name attribute was missing) this field
+ * is set to "<unknown>". */
+ const char* routine_name;
+
+ /* Name of the source file containing the routine. If source location for the
+ * routine was not available, this field is set to NULL, and content of
+ * dir_name, and line_number fields of this structure is not defined. */
+ const char* file_name;
+
+ /* Path to the source file directory. If file_name field of this structure is
+ * NULL, content of this field is not defined. */
+ const char* dir_name;
+
+ /* Line number in the source file for the address. If file_name field of this
+ * structure is NULL, content of this field is not defined. */
+ uint32_t line_number;
+
+ /* If routine that contains the given address has been inlined (or it is part
+ * of even deeper inline branch) this array lists information about that
+ * inline branch rooting to the first routine that has not been inlined. The
+ * first element in the array references a routine, where routine containing
+ * the given address has been inlined. The second entry contains information
+ * about a routine referenced by the first entry (and so on). If routine,
+ * containing the given address has not been inlined, this field is set to
+ * NULL. The array ends with an entry containing all zeroes. */
+ Elf_InlineInfo* inline_stack;
+} Elf_AddressInfo;
+
+//=============================================================================
+// API routines
+//=============================================================================
+
+/* Initializes ELFF API for the given ELF file.
+ * Param:
+ * elf_file_path - Path to the ELF file to initialize API for.
+ * Return:
+ * On success, this routine returns a handle that can be used in subsequent
+ * calls to this API dealing with the given ELF file. On failure this routine
+ * returns NULL, with errno providing extended error information.
+ * NOTE: handle returned from this routine must be closed using elff_close().
+ */
+ELFF_HANDLE elff_init(const char* elf_file_path);
+
+/* Closes a handle obtained after successful call to elff_init routine.
+ * Param:
+ * handle - A handle to close. This handle must be a handle returned from
+ * a successful call to elff_init routine.
+ */
+void elff_close(ELFF_HANDLE handle);
+
+/* Checks if ELF file represents an executable file, or a shared library.
+ * handle - A handle obtained from successful call to elff_init().
+ * Return:
+ * 1 if ELF file represents an executable file, or
+ * 0 if ELF file represents a shared library, or
+ * -1 if handle is invalid.
+ */
+int elff_is_exec(ELFF_HANDLE handle);
+
+/* Gets PC address information.
+ * Param:
+ * handle - A handle obtained from successful call to elff_init().
+ * address - PC address to get information for. Address must be relative to
+ * the beginning of ELF file represented by the handle parameter.
+ * address_info - Upon success contains information about routine(s) that
+ * contain the given address.
+ * Return:
+ * 0 if routine(s) containing the given address has been found and information
+ * has been saved into address_info, or -1 if no appropriate routine for that
+ * address has been found, or there was a memory error when collecting
+ * routine(s) information. In case of failure, errno provides extended
+ * error information.
+ * NOTE: Successful call to this routine must be complimented with a call
+ * to free_pc_address_info, so ELFF API can release resources aquired for
+ * address_info.
+ */
+int elff_get_pc_address_info(ELFF_HANDLE handle,
+ uint64_t address,
+ Elf_AddressInfo* address_info);
+
+/* Frees resources acquired for address information in successful call to
+ * get_pc_address_info().
+ * Param:
+ * handle - A handle obtained from successful call to elff_init().
+ * address_info - Address information structure, initialized in successful
+ * call to get_pc_address_info() routine.
+ */
+void elff_free_pc_address_info(ELFF_HANDLE handle,
+ Elf_AddressInfo* address_info);
+
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+
+#endif // ELFF_API_H_
@@ -76,7 +76,7 @@ int cpu_restore_state_copy(struct TranslationBlock *tb, void *puc); void cpu_resume_from_signal(CPUState *env1, void *puc); void cpu_io_recompile(CPUState *env, void *retaddr); -TranslationBlock *tb_gen_code(CPUState *env, +TranslationBlock *tb_gen_code(CPUState *env, target_ulong pc, target_ulong cs_base, int flags, int cflags); void cpu_exec_init(CPUState *env); @@ -154,11 +154,23 @@ struct TranslationBlock { jmp_first */ struct TranslationBlock *jmp_next[2]; struct TranslationBlock *jmp_first; - #ifdef CONFIG_TRACE struct BBRec *bb_rec; uint64_t prev_time; #endif + +#ifdef CONFIG_MEMCHECK + /* Maps PCs in this translation block to corresponding PCs in guest address + * space. The array is arranged in such way, that every even entry contains + * PC in the translation block, followed by an odd entry that contains + * guest PC corresponding to that PC in the translation block. This + * arrangement is set by tcg_gen_code_common that initializes this array + * when performing guest code translation. */ + target_ulong* tpc2gpc; + /* Number of pairs (pc_tb, pc_guest) in tpc2gpc array. */ + unsigned int tpc2gpc_pairs; +#endif // CONFIG_MEMCHECK + uint32_t icount; }; @@ -182,6 +194,60 @@ static inline unsigned int tb_phys_hash_func(unsigned long pc) return pc & (CODE_GEN_PHYS_HASH_SIZE - 1); } +#ifdef CONFIG_MEMCHECK +/* Gets translated PC for a given (translated PC, guest PC) pair. + * Return: + * Translated PC, or NULL if pair index was too large. + */ +static inline target_ulong +tb_get_tb_pc(const TranslationBlock* tb, unsigned int pair) +{ + return (tb->tpc2gpc != NULL && pair < tb->tpc2gpc_pairs) ? + tb->tpc2gpc[pair * 2] : 0; +} + +/* Gets guest PC for a given (translated PC, guest PC) pair. + * Return: + * Guest PC, or NULL if pair index was too large. + */ +static inline target_ulong +tb_get_guest_pc(const TranslationBlock* tb, unsigned int pair) +{ + return (tb->tpc2gpc != NULL && pair < tb->tpc2gpc_pairs) ? + tb->tpc2gpc[pair * 2 + 1] : 0; +} + +/* Gets guest PC for a given translated PC. + * Return: + * Guest PC for a given translated PC, or NULL if there was no pair, matching + * translated PC in tb's tpc2gpc array. + */ +static inline target_ulong +tb_search_guest_pc_from_tb_pc(const TranslationBlock* tb, target_ulong tb_pc) +{ + if (tb->tpc2gpc != NULL && tb->tpc2gpc_pairs != 0) { + unsigned int m_min = 0; + unsigned int m_max = (tb->tpc2gpc_pairs - 1) << 1; + /* Make sure that tb_pc is within TB array. */ + if (tb_pc < tb->tpc2gpc[0]) { + return 0; + } + while (m_min <= m_max) { + const unsigned int m = ((m_min + m_max) >> 1) & ~1; + if (tb_pc < tb->tpc2gpc[m]) { + m_max = m - 2; + } else if (m == m_max || tb_pc < tb->tpc2gpc[m + 2]) { + return tb->tpc2gpc[m + 1]; + } else { + m_min = m + 2; + } + } + return tb->tpc2gpc[m_max + 1]; + } + return 0; +} +#endif // CONFIG_MEMCHECK + TranslationBlock *tb_alloc(target_ulong pc); void tb_free(TranslationBlock *tb); void tb_flush(CPUState *env); @@ -368,7 +434,7 @@ void kqemu_flush_page(CPUState *env, target_ulong addr); void kqemu_flush(CPUState *env, int global); void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr); void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr); -void kqemu_set_phys_mem(uint64_t start_addr, ram_addr_t size, +void kqemu_set_phys_mem(uint64_t start_addr, ram_addr_t size, ram_addr_t phys_offset); void kqemu_cpu_interrupt(CPUState *env); void kqemu_record_dump(void); @@ -43,6 +43,9 @@ #if defined(CONFIG_USER_ONLY) #include <qemu.h> #endif +#ifdef CONFIG_MEMCHECK +#include "memcheck/memcheck_api.h" +#endif // CONFIG_MEMCHECK //#define DEBUG_TB_INVALIDATE //#define DEBUG_FLUSH @@ -216,21 +219,21 @@ static void map_exec(void *addr, long size) DWORD old_protect; VirtualProtect(addr, size, PAGE_EXECUTE_READWRITE, &old_protect); - + } #else static void map_exec(void *addr, long size) { unsigned long start, end, page_size; - + page_size = getpagesize(); start = (unsigned long)addr; start &= ~(page_size - 1); - + end = (unsigned long)addr + size; end += page_size - 1; end &= ~(page_size - 1); - + mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC); } @@ -280,7 +283,7 @@ static void page_init(void) (1ULL << TARGET_PHYS_ADDR_SPACE_BITS) - 1); page_set_flags(startaddr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(endaddr), - PAGE_RESERVED); + PAGE_RESERVED); } } while (!feof(f)); fclose(f); @@ -321,7 +324,7 @@ static inline PageDesc *page_find_alloc(target_ulong index) unsigned long addr = h2g(p); page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + len), - PAGE_RESERVED); + PAGE_RESERVED); } #else p = qemu_mallocz(sizeof(PageDesc) * L2_SIZE); @@ -429,7 +432,7 @@ static void code_gen_alloc(unsigned long tb_size) code_gen_buffer_size = MIN_CODE_GEN_BUFFER_SIZE; /* The code gen buffer location may have constraints depending on the host cpu and OS */ -#if defined(__linux__) +#if defined(__linux__) { int flags; void *start = NULL; @@ -476,7 +479,7 @@ static void code_gen_alloc(unsigned long tb_size) code_gen_buffer_size = (800 * 1024 * 1024); #endif code_gen_buffer = mmap(addr, code_gen_buffer_size, - PROT_WRITE | PROT_READ | PROT_EXEC, + PROT_WRITE | PROT_READ | PROT_EXEC, flags, -1, 0); if (code_gen_buffer == MAP_FAILED) { fprintf(stderr, "Could not allocate dynamic translator buffer\n"); @@ -489,7 +492,7 @@ static void code_gen_alloc(unsigned long tb_size) #endif #endif /* !USE_STATIC_CODE_GEN_BUFFER */ map_exec(code_gen_prologue, sizeof(code_gen_prologue)); - code_gen_buffer_max_size = code_gen_buffer_size - + code_gen_buffer_max_size = code_gen_buffer_size - code_gen_max_block_size(); code_gen_max_blocks = code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE; tbs = qemu_malloc(code_gen_max_blocks * sizeof(TranslationBlock)); @@ -630,6 +633,17 @@ void tb_flush(CPUState *env1) nb_tbs = 0; for(env = first_cpu; env != NULL; env = env->next_cpu) { +#ifdef CONFIG_MEMCHECK + int tb_to_clean; + for (tb_to_clean = 0; tb_to_clean < TB_JMP_CACHE_SIZE; tb_to_clean++) { + if (env->tb_jmp_cache[tb_to_clean] != NULL && + env->tb_jmp_cache[tb_to_clean]->tpc2gpc != NULL) { + qemu_free(env->tb_jmp_cache[tb_to_clean]->tpc2gpc); + env->tb_jmp_cache[tb_to_clean]->tpc2gpc = NULL; + env->tb_jmp_cache[tb_to_clean]->tpc2gpc_pairs = 0; + } + } +#endif // CONFIG_MEMCHECK memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *)); } @@ -820,6 +834,14 @@ void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr) } tb->jmp_first = (TranslationBlock *)((long)tb | 2); /* fail safe */ +#ifdef CONFIG_MEMCHECK + if (tb->tpc2gpc != NULL) { + qemu_free(tb->tpc2gpc); + tb->tpc2gpc = NULL; + tb->tpc2gpc_pairs = 0; + } +#endif // CONFIG_MEMCHECK + tb_phys_invalidate_count++; } @@ -1185,6 +1207,10 @@ TranslationBlock *tb_alloc(target_ulong pc) tb = &tbs[nb_tbs++]; tb->pc = pc; tb->cflags = 0; +#ifdef CONFIG_MEMCHECK + tb->tpc2gpc = NULL; + tb->tpc2gpc_pairs = 0; +#endif // CONFIG_MEMCHECK return tb; } @@ -1745,11 +1771,11 @@ static inline void tlb_flush_jmp_cache(CPUState *env, target_ulong addr) /* Discard jump cache entries for any tb which might potentially overlap the flushed page. */ i = tb_jmp_cache_hash_page(addr - TARGET_PAGE_SIZE); - memset (&env->tb_jmp_cache[i], 0, + memset (&env->tb_jmp_cache[i], 0, TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); i = tb_jmp_cache_hash_page(addr); - memset (&env->tb_jmp_cache[i], 0, + memset (&env->tb_jmp_cache[i], 0, TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); } @@ -2076,6 +2102,37 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, } else { te->addr_write = -1; } + +#ifdef CONFIG_MEMCHECK + /* + * If we have memchecker running, we need to make sure that page, cached + * into TLB as the result of this operation will comply with our requirement + * to cause __ld/__stx_mmu being called for memory access on the pages + * containing memory blocks that require access violation checks. + * + * We need to check with memory checker if we should invalidate this page + * iff: + * - Memchecking is enabled. + * - Page that's been cached belongs to the user space. + * - Request to cache this page didn't come from softmmu. We're covered + * there, because after page was cached here we will invalidate it in + * the __ld/__stx_mmu wrapper. + * - Cached page belongs to RAM, not I/O area. + * - Page is cached for read, or write access. + */ + if (memcheck_instrument_mmu && mmu_idx == 1 && !is_softmmu && + (pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM && + (prot & (PAGE_READ | PAGE_WRITE)) && + memcheck_is_checked(vaddr & TARGET_PAGE_MASK, TARGET_PAGE_SIZE)) { + if (prot & PAGE_READ) { + te->addr_read ^= TARGET_PAGE_MASK; + } + if (prot & PAGE_WRITE) { + te->addr_write ^= TARGET_PAGE_MASK; + } + } +#endif // CONFIG_MEMCHECK + return ret; } @@ -3651,7 +3708,7 @@ void cpu_io_recompile(CPUState *env, void *retaddr) tb = tb_find_pc((unsigned long)retaddr); if (!tb) { - cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p", + cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p", retaddr); } n = env->icount_decr.u16.low + tb->icount; @@ -3729,7 +3786,7 @@ void dump_exec_info(FILE *f, cpu_fprintf(f, "Translation buffer state:\n"); cpu_fprintf(f, "gen code size %ld/%ld\n", code_gen_ptr - code_gen_buffer, code_gen_buffer_max_size); - cpu_fprintf(f, "TB count %d/%d\n", + cpu_fprintf(f, "TB count %d/%d\n", nb_tbs, code_gen_max_blocks); cpu_fprintf(f, "TB avg target size %d max=%d bytes\n", nb_tbs ? target_code_size / nb_tbs : 0, diff --git a/hw/android_arm.c b/hw/android_arm.c index 4ef4a10..d656b81 100644 --- a/hw/android_arm.c +++ b/hw/android_arm.c @@ -20,6 +20,9 @@ #include "audio/audio.h" #include "arm-misc.h" #include "console.h" +#ifdef CONFIG_MEMCHECK +#include "memcheck/memcheck_api.h" +#endif // CONFIG_MEMCHECK #define ARM_CPU_SAVE_VERSION 1 @@ -63,7 +66,7 @@ uint32_t switch_test_write(void *opaque, uint32_t state) #endif static void android_arm_init_(ram_addr_t ram_size, const char *boot_device, - const char *kernel_filename, + const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) @@ -142,9 +145,19 @@ static void android_arm_init_(ram_addr_t ram_size, #endif #ifdef CONFIG_TRACE extern const char *trace_filename; - if (trace_filename != NULL) { + /* Init trace device if either tracing, or memory checking is enabled. */ + if (trace_filename != NULL +#ifdef CONFIG_MEMCHECK + || memcheck_enabled +#endif // CONFIG_MEMCHECK + ) { trace_dev_init(); } + if (trace_filename != NULL) { + dprint( "Trace file name is set to %s\n", trace_filename ); + } else { + dprint("Trace file name is not set\n"); + } #endif #if TEST_SWITCH diff --git a/hw/goldfish_trace.c b/hw/goldfish_trace.c index c4f2f92..742ac63 100644 --- a/hw/goldfish_trace.c +++ b/hw/goldfish_trace.c @@ -15,12 +15,16 @@ */ #include "qemu_file.h" #include "goldfish_trace.h" +#ifdef CONFIG_MEMCHECK +#include "memcheck/memcheck.h" +#endif // CONFIG_MEMCHECK //#define DEBUG 1 extern void cpu_loop_exit(void); extern int tracing; +extern const char *trace_filename; /* for execve */ static char path[CLIENT_PAGE_SIZE]; @@ -44,28 +48,51 @@ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t va switch (offset >> 2) { case TRACE_DEV_REG_SWITCH: // context switch, switch to pid - trace_switch(value); + if (trace_filename != NULL) { + trace_switch(value); #ifdef DEBUG - printf("QEMU.trace: kernel, context switch %u\n", value); + printf("QEMU.trace: kernel, context switch %u\n", value); #endif + } +#ifdef CONFIG_MEMCHECK + if (memcheck_enabled) { + memcheck_switch(value); + } +#endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_TGID: // save the tgid for the following fork/clone tgid = value; #ifdef DEBUG - printf("QEMU.trace: kernel, tgid %u\n", value); + if (trace_filename != NULL) { + printf("QEMU.trace: kernel, tgid %u\n", value); + } #endif break; case TRACE_DEV_REG_FORK: // fork, fork new pid - trace_fork(tgid, value); + if (trace_filename != NULL) { + trace_fork(tgid, value); #ifdef DEBUG - printf("QEMU.trace: kernel, fork %u\n", value); + printf("QEMU.trace: kernel, fork %u\n", value); #endif + } +#ifdef CONFIG_MEMCHECK + if (memcheck_enabled) { + memcheck_fork(tgid, value); + } +#endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_CLONE: // fork, clone new pid (i.e. thread) - trace_clone(tgid, value); + if (trace_filename != NULL) { + trace_clone(tgid, value); #ifdef DEBUG - printf("QEMU.trace: kernel, clone %u\n", value); + printf("QEMU.trace: kernel, clone %u\n", value); #endif + } +#ifdef CONFIG_MEMCHECK + if (memcheck_enabled) { + memcheck_clone(tgid, value); + } +#endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_EXECVE_VMSTART: // execve, vstart vstart = value; @@ -78,10 +105,23 @@ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t va break; case TRACE_DEV_REG_EXECVE_EXEPATH: // init exec, path of EXE vstrcpy(value, path, CLIENT_PAGE_SIZE); - trace_init_exec(vstart, vend, eoff, path); + if (trace_filename != NULL) { + trace_init_exec(vstart, vend, eoff, path); #ifdef DEBUG - printf("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path); + printf("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n", + vstart, vend, eoff, path); #endif + } +#ifdef CONFIG_MEMCHECK + if (memcheck_enabled) { + if (path[0] == '\0') { + // vstrcpy may fail to copy path. In this case lets do it + // differently. + memcheck_get_guest_kernel_string(path, value, CLIENT_PAGE_SIZE); + } + memcheck_mmap_exepath(vstart, vend, eoff, path); + } +#endif // CONFIG_MEMCHECK path[0] = 0; break; case TRACE_DEV_REG_CMDLINE_LEN: // execve, process cmdline length @@ -89,23 +129,37 @@ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t va break; case TRACE_DEV_REG_CMDLINE: // execve, process cmdline cpu_memory_rw_debug(cpu_single_env, value, arg, cmdlen, 0); - trace_execve(arg, cmdlen); + if (trace_filename != NULL) { + trace_execve(arg, cmdlen); + } +#ifdef CONFIG_MEMCHECK + if (memcheck_enabled) { + memcheck_set_cmd_line(arg, cmdlen); + } +#endif // CONFIG_MEMCHECK #ifdef DEBUG - { + if (trace_filename != NULL) { int i; for (i = 0; i < cmdlen; i ++) if (i != cmdlen - 1 && arg[i] == 0) arg[i] = ' '; printf("QEMU.trace: kernel, execve %s[%d]\n", arg, cmdlen); + arg[0] = 0; } #endif - arg[0] = 0; break; case TRACE_DEV_REG_EXIT: // exit, exit current process with exit code - trace_exit(value); + if (trace_filename != NULL) { + trace_exit(value); #ifdef DEBUG - printf("QEMU.trace: kernel, exit %x\n", value); + printf("QEMU.trace: kernel, exit %x\n", value); #endif + } +#ifdef CONFIG_MEMCHECK + if (memcheck_enabled) { + memcheck_exit(value); + } +#endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_NAME: // record thread name vstrcpy(value, path, CLIENT_PAGE_SIZE); @@ -115,28 +169,49 @@ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t va if (path[len - 1] == '\n') { path[len - 1] = 0; } - trace_name(path); + if (trace_filename != NULL) { + trace_name(path); #ifdef DEBUG - printf("QEMU.trace: kernel, name %s\n", path); + printf("QEMU.trace: kernel, name %s\n", path); #endif + } break; case TRACE_DEV_REG_MMAP_EXEPATH: // mmap, path of EXE, the others are same as execve vstrcpy(value, path, CLIENT_PAGE_SIZE); - trace_mmap(vstart, vend, eoff, path); + if (trace_filename != NULL) { + trace_mmap(vstart, vend, eoff, path); #ifdef DEBUG - printf("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path); + printf("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path); #endif + } +#ifdef CONFIG_MEMCHECK + if (memcheck_enabled) { + if (path[0] == '\0') { + // vstrcpy may fail to copy path. In this case lets do it + // differently. + memcheck_get_guest_kernel_string(path, value, CLIENT_PAGE_SIZE); + } + memcheck_mmap_exepath(vstart, vend, eoff, path); + } +#endif // CONFIG_MEMCHECK path[0] = 0; break; case TRACE_DEV_REG_INIT_PID: // init, name the pid that starts before device registered pid = value; +#ifdef CONFIG_MEMCHECK + if (memcheck_enabled) { + memcheck_init_pid(value); + } +#endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_INIT_NAME: // init, the comm of the init pid vstrcpy(value, path, CLIENT_PAGE_SIZE); - trace_init_name(tgid, pid, path); + if (trace_filename != NULL) { + trace_init_name(tgid, pid, path); #ifdef DEBUG - printf("QEMU.trace: kernel, init name %u [%s]\n", pid, path); + printf("QEMU.trace: kernel, init name %u [%s]\n", pid, path); #endif + } path[0] = 0; break; @@ -145,18 +220,21 @@ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t va break; case TRACE_DEV_REG_DYN_SYM: // add dynamic symbol vstrcpy(value, arg, CLIENT_PAGE_SIZE); - trace_dynamic_symbol_add(dsaddr, arg); + if (trace_filename != NULL) { + trace_dynamic_symbol_add(dsaddr, arg); #ifdef DEBUG - printf("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, arg); + printf("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, arg); #endif + } arg[0] = 0; break; case TRACE_DEV_REG_REMOVE_ADDR: // remove dynamic symbol addr - trace_dynamic_symbol_remove(value); + if (trace_filename != NULL) { + trace_dynamic_symbol_remove(value); #ifdef DEBUG - printf("QEMU.trace: dynamic symbol remove %lx\n", dsaddr); + printf("QEMU.trace: dynamic symbol remove %lx\n", dsaddr); #endif - arg[0] = 0; + } break; case TRACE_DEV_REG_PRINT_STR: // print string @@ -172,10 +250,11 @@ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t va break; case TRACE_DEV_REG_STOP_EMU: // stop the VM execution - // To ensure that the number of instructions executed in this - // block is correct, we pretend that there was an exception. - trace_exception(0); - + if (trace_filename != NULL) { + // To ensure that the number of instructions executed in this + // block is correct, we pretend that there was an exception. + trace_exception(0); + } cpu_single_env->exception_index = EXCP_HLT; cpu_single_env->halted = 1; qemu_system_shutdown_request(); @@ -183,14 +262,19 @@ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t va break; case TRACE_DEV_REG_ENABLE: // tracing enable: 0 = stop, 1 = start - if (value == 1) - start_tracing(); + if (value == 1) { + if (trace_filename != NULL) { + start_tracing(); + } + } else if (value == 0) { - stop_tracing(); + if (trace_filename != NULL) { + stop_tracing(); - // To ensure that the number of instructions executed in this - // block is correct, we pretend that there was an exception. - trace_exception(0); + // To ensure that the number of instructions executed in this + // block is correct, we pretend that there was an exception. + trace_exception(0); + } } break; @@ -198,7 +282,14 @@ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t va unmap_start = value; break; case TRACE_DEV_REG_UNMAP_END: - trace_munmap(unmap_start, value); + if (trace_filename != NULL) { + trace_munmap(unmap_start, value); + } +#ifdef CONFIG_MEMCHECK + if (memcheck_enabled) { + memcheck_unmap(unmap_start, value); + } +#endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_METHOD_ENTRY: @@ -207,11 +298,45 @@ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t va case TRACE_DEV_REG_NATIVE_ENTRY: case TRACE_DEV_REG_NATIVE_EXIT: case TRACE_DEV_REG_NATIVE_EXCEPTION: - if (tracing) { - int call_type = (offset - 4096) >> 2; - trace_interpreted_method(value, call_type); + if (trace_filename != NULL) { + if (tracing) { + int call_type = (offset - 4096) >> 2; + trace_interpreted_method(value, call_type); + } + } + break; + +#ifdef CONFIG_MEMCHECK + case TRACE_DEV_REG_MALLOC: + if (memcheck_enabled) { + memcheck_guest_alloc(value); + } + break; + + case TRACE_DEV_REG_FREE_PTR: + if (memcheck_enabled) { + memcheck_guest_free(value); + } + break; + + case TRACE_DEV_REG_QUERY_MALLOC: + if (memcheck_enabled) { + memcheck_guest_query_malloc(value); + } + break; + + case TRACE_DEV_REG_LIBC_INIT: + if (memcheck_enabled) { + memcheck_guest_libc_initialized(value); + } + break; + + case TRACE_DEV_REG_PRINT_USER_STR: + if (memcheck_enabled) { + memcheck_guest_print_str(value); } break; +#endif // CONFIG_MEMCHECK default: if (offset < 4096) { 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(¤t_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 diff --git a/softmmu_header.h b/softmmu_header.h index a1b3808..f96b512 100644 --- a/softmmu_header.h +++ b/softmmu_header.h @@ -41,8 +41,16 @@ #if ACCESS_TYPE < (NB_MMU_MODES) -#define CPU_MMU_INDEX ACCESS_TYPE +#if defined(OUTSIDE_JIT) +/* Dispatch calls to __ldx_outside_jit / __stx_outside_jit, which don't + * expect CPU environment. to be cached in ebp register, but rather uses + * cpu_single_env variable for that purpose. + */ +#define MMUSUFFIX _outside_jit +#else // OUTSIDE_JIT #define MMUSUFFIX _mmu +#endif // OUTSIDE_JIT +#define CPU_MMU_INDEX ACCESS_TYPE #elif ACCESS_TYPE == (NB_MMU_MODES) diff --git a/softmmu_outside_jit.c b/softmmu_outside_jit.c new file mode 100644 index 0000000..048a2ea --- /dev/null +++ b/softmmu_outside_jit.c @@ -0,0 +1,48 @@ +/* Copyright (C) 2007-2009 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 SOFTMMU macros expansion for ldx_user and stx_user routines used + * outside of JIT. The issue is that regular implementation of these routines + * assumes that pointer to CPU environment is stored in ebp register, which + * is true for calls made inside JIT, but is not necessarily true for calls + * made outside of JIT. The way SOFTMMU macros are expanded in this header + * enforces ldx/stx routines to use CPU environment stored in cpu_single_env + * variable. + */ + +#include "qemu-common.h" +#include "cpu.h" + +#define OUTSIDE_JIT +#define MMUSUFFIX _outside_jit +#define GETPC() NULL +#define env cpu_single_env +#define ACCESS_TYPE 1 +#define CPU_MMU_INDEX (cpu_mmu_index(env)) + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" + +#undef CPU_MMU_INDEX +#undef ACCESS_TYPE +#undef env +#undef MMUSUFFIX diff --git a/softmmu_outside_jit.h b/softmmu_outside_jit.h new file mode 100644 index 0000000..07ba4c7 --- /dev/null +++ b/softmmu_outside_jit.h @@ -0,0 +1,98 @@ +/* Copyright (C) 2007-2009 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 SOFTMMU macros expansion for ldx_user and stx_user routines used + * outside of JIT. The issue is that regular implementation of these routines + * assumes that pointer to CPU environment is stored in ebp register, which + * is true for calls made inside JIT, but is not necessarily true for calls + * made outside of JIT. The way SOFTMMU macros are expanded in this header + * enforces ldx/stx routines to use CPU environment stored in cpu_single_env + * variable. + */ +#ifndef QEMU_SOFTMMU_OUTSIDE_JIT_H +#define QEMU_SOFTMMU_OUTSIDE_JIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Declares routines implemented in softmmu_outside_jit.c, that are used in +// this macros expansion. Note that MMUSUFFIX _outside_jit is enforced in +// softmmu_header.h by defining OUTSIDE_JIT macro. +//////////////////////////////////////////////////////////////////////////////// + +uint8_t REGPARM __ldb_outside_jit(target_ulong addr, int mmu_idx); +void REGPARM __stb_outside_jit(target_ulong addr, uint8_t val, int mmu_idx); +uint16_t REGPARM __ldw_outside_jit(target_ulong addr, int mmu_idx); +void REGPARM __stw_outside_jit(target_ulong addr, uint16_t val, int mmu_idx); +uint32_t REGPARM __ldl_outside_jit(target_ulong addr, int mmu_idx); +void REGPARM __stl_outside_jit(target_ulong addr, uint32_t val, int mmu_idx); +uint64_t REGPARM __ldq_outside_jit(target_ulong addr, int mmu_idx); +void REGPARM __stq_outside_jit(target_ulong addr, uint64_t val, int mmu_idx); + +// Enforces MMUSUFFIX to be set to _outside_jit in softmmu_header.h +#define OUTSIDE_JIT +// Enforces use of cpu_single_env for CPU environment. +#define env cpu_single_env + +// ============================================================================= +// Generate ld/stx_user +// ============================================================================= +#define MEMSUFFIX MMU_MODE1_SUFFIX +#define ACCESS_TYPE 1 + +#define DATA_SIZE 1 +#include "softmmu_header.h" + +#define DATA_SIZE 2 +#include "softmmu_header.h" + +#define DATA_SIZE 4 +#include "softmmu_header.h" + +#define DATA_SIZE 8 +#include "softmmu_header.h" + +#undef MEMSUFFIX +#undef ACCESS_TYPE + +// ============================================================================= +// Generate ld/stx_kernel +// ============================================================================= +#define MEMSUFFIX MMU_MODE0_SUFFIX +#define ACCESS_TYPE 0 + +#define DATA_SIZE 1 +#include "softmmu_header.h" + +#define DATA_SIZE 2 +#include "softmmu_header.h" + +#define DATA_SIZE 4 +#include "softmmu_header.h" + +#define DATA_SIZE 8 +#include "softmmu_header.h" + +#undef MEMSUFFIX +#undef ACCESS_TYPE + +#undef env +#undef OUTSIDE_JIT + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +#endif // QEMU_SOFTMMU_OUTSIDE_JIT_H diff --git a/softmmu_template.h b/softmmu_template.h index 4b33aef..ad00c4d 100644 --- a/softmmu_template.h +++ b/softmmu_template.h @@ -47,6 +47,20 @@ #define ADDR_READ addr_read #endif +#if defined(CONFIG_MEMCHECK) && !defined(OUTSIDE_JIT) && !defined(SOFTMMU_CODE_ACCESS) +/* + * Support for memory access checker. + * We need to instrument __ldx/__stx_mmu routines implemented in this file with + * callbacks to access validation routines implemented by the memory checker. + * Note that (at least for now) we don't do that instrumentation for memory + * addressing the code (SOFTMMU_CODE_ACCESS controls that). Also, we don't want + * to instrument code that is used by emulator itself (OUTSIDE_JIT controls + * that). + */ +#define CONFIG_MEMCHECK_MMU +#include "memcheck/memcheck_api.h" +#endif // CONFIG_MEMCHECK && !OUTSIDE_JIT && !SOFTMMU_CODE_ACCESS + static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr, int mmu_idx, void *retaddr); @@ -91,6 +105,9 @@ DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr, target_ulong tlb_addr; target_phys_addr_t addend; void *retaddr; +#ifdef CONFIG_MEMCHECK_MMU + int invalidate_cache = 0; +#endif // CONFIG_MEMCHECK_MMU /* test if there is match for unaligned or IO access */ /* XXX: could done more in memory macro in a non portable way */ @@ -106,6 +123,17 @@ DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr, addend = env->iotlb[mmu_idx][index]; res = glue(io_read, SUFFIX)(addend, addr, retaddr); } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { + /* This is not I/O access: do access verification. */ +#ifdef CONFIG_MEMCHECK_MMU + /* We only validate access to the guest's user space, for which + * mmu_idx is set to 1. */ + if (memcheck_instrument_mmu && mmu_idx == 1 && + memcheck_validate_ld(addr, DATA_SIZE, (target_ulong)GETPC())) { + /* Memory read breaks page boundary. So, if required, we + * must invalidate two caches in TLB. */ + invalidate_cache = 2; + } +#endif // CONFIG_MEMCHECK_MMU /* slow unaligned access (it spans two pages or IO) */ do_unaligned_access: retaddr = GETPC(); @@ -115,6 +143,14 @@ DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr, res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr, mmu_idx, retaddr); } else { +#ifdef CONFIG_MEMCHECK_MMU + /* We only validate access to the guest's user space, for which + * mmu_idx is set to 1. */ + if (memcheck_instrument_mmu && mmu_idx == 1) { + invalidate_cache = memcheck_validate_ld(addr, DATA_SIZE, + (target_ulong)GETPC()); + } +#endif // CONFIG_MEMCHECK_MMU /* unaligned/aligned access in the same page */ #ifdef ALIGNED_ONLY if ((addr & (DATA_SIZE - 1)) != 0) { @@ -125,6 +161,20 @@ DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr, addend = env->tlb_table[mmu_idx][index].addend; res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)(addr+addend)); } +#ifdef CONFIG_MEMCHECK_MMU + if (invalidate_cache) { + /* Accessed memory is under memchecker control. We must invalidate + * containing page(s) in order to make sure that next access to them + * will invoke _ld/_st_mmu. */ + env->tlb_table[mmu_idx][index].addr_read ^= TARGET_PAGE_MASK; + env->tlb_table[mmu_idx][index].addr_write ^= TARGET_PAGE_MASK; + if ((invalidate_cache == 2) && (index < CPU_TLB_SIZE)) { + // Read crossed page boundaris. Invalidate second cache too. + env->tlb_table[mmu_idx][index + 1].addr_read ^= TARGET_PAGE_MASK; + env->tlb_table[mmu_idx][index + 1].addr_write ^= TARGET_PAGE_MASK; + } + } +#endif // CONFIG_MEMCHECK_MMU } else { /* the page is not in the TLB : fill it */ retaddr = GETPC(); @@ -234,6 +284,9 @@ void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, target_ulong tlb_addr; void *retaddr; int index; +#ifdef CONFIG_MEMCHECK_MMU + int invalidate_cache = 0; +#endif // CONFIG_MEMCHECK_MMU index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); redo: @@ -247,6 +300,18 @@ void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, addend = env->iotlb[mmu_idx][index]; glue(io_write, SUFFIX)(addend, val, addr, retaddr); } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { + /* This is not I/O access: do access verification. */ +#ifdef CONFIG_MEMCHECK_MMU + /* We only validate access to the guest's user space, for which + * mmu_idx is set to 1. */ + if (memcheck_instrument_mmu && mmu_idx == 1 && + memcheck_validate_st(addr, DATA_SIZE, (uint64_t)val, + (target_ulong)GETPC())) { + /* Memory write breaks page boundary. So, if required, we + * must invalidate two caches in TLB. */ + invalidate_cache = 2; + } +#endif // CONFIG_MEMCHECK_MMU do_unaligned_access: retaddr = GETPC(); #ifdef ALIGNED_ONLY @@ -255,6 +320,15 @@ void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, glue(glue(slow_st, SUFFIX), MMUSUFFIX)(addr, val, mmu_idx, retaddr); } else { +#ifdef CONFIG_MEMCHECK_MMU + /* We only validate access to the guest's user space, for which + * mmu_idx is set to 1. */ + if (memcheck_instrument_mmu && mmu_idx == 1) { + invalidate_cache = memcheck_validate_st(addr, DATA_SIZE, + (uint64_t)val, + (target_ulong)GETPC()); + } +#endif // CONFIG_MEMCHECK_MMU /* aligned/unaligned access in the same page */ #ifdef ALIGNED_ONLY if ((addr & (DATA_SIZE - 1)) != 0) { @@ -265,6 +339,20 @@ void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, addend = env->tlb_table[mmu_idx][index].addend; glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)(addr+addend), val); } +#ifdef CONFIG_MEMCHECK_MMU + if (invalidate_cache) { + /* Accessed memory is under memchecker control. We must invalidate + * containing page(s) in order to make sure that next access to them + * will invoke _ld/_st_mmu. */ + env->tlb_table[mmu_idx][index].addr_read ^= TARGET_PAGE_MASK; + env->tlb_table[mmu_idx][index].addr_write ^= TARGET_PAGE_MASK; + if ((invalidate_cache == 2) && (index < CPU_TLB_SIZE)) { + // Write crossed page boundaris. Invalidate second cache too. + env->tlb_table[mmu_idx][index + 1].addr_read ^= TARGET_PAGE_MASK; + env->tlb_table[mmu_idx][index + 1].addr_write ^= TARGET_PAGE_MASK; + } + } +#endif // CONFIG_MEMCHECK_MMU } else { /* the page is not in the TLB : fill it */ retaddr = GETPC(); diff --git a/sys-tree.h b/sys-tree.h new file mode 100644 index 0000000..13d049f --- /dev/null +++ b/sys-tree.h @@ -0,0 +1,771 @@ +/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* $FreeBSD: src/sys/sys/tree.h,v 1.9.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $ */ + +/*- + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* Ommit "struct" prefix if this code is built with C++, + * so it can build. */ +#ifdef __cplusplus +#define SYS_TREE_STRUCT +#else +#define SYS_TREE_STRUCT struct +#endif + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + SYS_TREE_STRUCT type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + SYS_TREE_STRUCT type *spe_left; /* left element */ \ + SYS_TREE_STRUCT type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(SYS_TREE_STRUCT name *, SYS_TREE_STRUCT type *); \ +void name##_SPLAY_MINMAX(SYS_TREE_STRUCT name *, int); \ +SYS_TREE_STRUCT type *name##_SPLAY_INSERT(SYS_TREE_STRUCT name *, SYS_TREE_STRUCT type *); \ +SYS_TREE_STRUCT type *name##_SPLAY_REMOVE(SYS_TREE_STRUCT name *, SYS_TREE_STRUCT type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline SYS_TREE_STRUCT type * \ +name##_SPLAY_FIND(SYS_TREE_STRUCT name *head, SYS_TREE_STRUCT type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline SYS_TREE_STRUCT type * \ +name##_SPLAY_NEXT(SYS_TREE_STRUCT name *head, SYS_TREE_STRUCT type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline SYS_TREE_STRUCT type * \ +name##_SPLAY_MIN_MAX(SYS_TREE_STRUCT name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +SYS_TREE_STRUCT type * \ +name##_SPLAY_INSERT(SYS_TREE_STRUCT name *head, SYS_TREE_STRUCT type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +SYS_TREE_STRUCT type * \ +name##_SPLAY_REMOVE(SYS_TREE_STRUCT name *head, SYS_TREE_STRUCT type *elm) \ +{ \ + SYS_TREE_STRUCT type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(SYS_TREE_STRUCT name *head, SYS_TREE_STRUCT type *elm) \ +{ \ + SYS_TREE_STRUCT type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(SYS_TREE_STRUCT name *head, int __comp) \ +{ \ + SYS_TREE_STRUCT type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + SYS_TREE_STRUCT type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + SYS_TREE_STRUCT type *rbe_left; /* left element */ \ + SYS_TREE_STRUCT type *rbe_right; /* right element */ \ + SYS_TREE_STRUCT type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(SYS_TREE_STRUCT name *, SYS_TREE_STRUCT type *); \ +attr void name##_RB_REMOVE_COLOR(SYS_TREE_STRUCT name *, SYS_TREE_STRUCT type *, SYS_TREE_STRUCT type *);\ +attr SYS_TREE_STRUCT type *name##_RB_REMOVE(SYS_TREE_STRUCT name *, SYS_TREE_STRUCT type *); \ +attr SYS_TREE_STRUCT type *name##_RB_INSERT(SYS_TREE_STRUCT name *, SYS_TREE_STRUCT type *); \ +attr SYS_TREE_STRUCT type *name##_RB_FIND(SYS_TREE_STRUCT name *, SYS_TREE_STRUCT type *); \ +attr SYS_TREE_STRUCT type *name##_RB_NFIND(SYS_TREE_STRUCT name *, SYS_TREE_STRUCT type *); \ +attr SYS_TREE_STRUCT type *name##_RB_NEXT(SYS_TREE_STRUCT type *); \ +attr SYS_TREE_STRUCT type *name##_RB_PREV(SYS_TREE_STRUCT type *); \ +attr SYS_TREE_STRUCT type *name##_RB_MINMAX(SYS_TREE_STRUCT name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(SYS_TREE_STRUCT name *head, SYS_TREE_STRUCT type *elm) \ +{ \ + SYS_TREE_STRUCT type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) != NULL && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(SYS_TREE_STRUCT name *head, SYS_TREE_STRUCT type *parent, SYS_TREE_STRUCT type *elm) \ +{ \ + SYS_TREE_STRUCT type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + SYS_TREE_STRUCT type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)) \ + != NULL) \ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + SYS_TREE_STRUCT type *oright; \ + if ((oright = RB_RIGHT(tmp, field)) \ + != NULL) \ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr SYS_TREE_STRUCT type * \ +name##_RB_REMOVE(SYS_TREE_STRUCT name *head, SYS_TREE_STRUCT type *elm) \ +{ \ + SYS_TREE_STRUCT type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + SYS_TREE_STRUCT type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field)) != NULL) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field)) != NULL); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr SYS_TREE_STRUCT type * \ +name##_RB_INSERT(SYS_TREE_STRUCT name *head, SYS_TREE_STRUCT type *elm) \ +{ \ + SYS_TREE_STRUCT type *tmp; \ + SYS_TREE_STRUCT type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr SYS_TREE_STRUCT type * \ +name##_RB_FIND(SYS_TREE_STRUCT name *head, SYS_TREE_STRUCT type *elm) \ +{ \ + SYS_TREE_STRUCT type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr SYS_TREE_STRUCT type * \ +name##_RB_NFIND(SYS_TREE_STRUCT name *head, SYS_TREE_STRUCT type *elm) \ +{ \ + SYS_TREE_STRUCT type *tmp = RB_ROOT(head); \ + SYS_TREE_STRUCT type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr SYS_TREE_STRUCT type * \ +name##_RB_NEXT(SYS_TREE_STRUCT type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr SYS_TREE_STRUCT type * \ +name##_RB_PREV(SYS_TREE_STRUCT type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr SYS_TREE_STRUCT type * \ +name##_RB_MINMAX(SYS_TREE_STRUCT name *head, int val) \ +{ \ + SYS_TREE_STRUCT type *tmp = RB_ROOT(head); \ + SYS_TREE_STRUCT type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#endif /* _SYS_TREE_H_ */ diff --git a/target-arm/cpu.h b/target-arm/cpu.h index f98655f..afc0146 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -417,6 +417,15 @@ static inline int cpu_mmu_index (CPUState *env) return (env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR ? 1 : 0; } +static inline int is_cpu_user (CPUState *env) +{ +#ifdef CONFIG_USER_ONLY + return 1; +#else + return (env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR; +#endif // CONFIG_USER_ONLY +} + #if defined(CONFIG_USER_ONLY) static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) { diff --git a/target-arm/helper.c b/target-arm/helper.c index 9ac7e25..56d9953 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -10,6 +10,9 @@ #ifdef CONFIG_TRACE #include "trace.h" #endif +#ifdef CONFIG_MEMCHECK +#include "memcheck/memcheck_api.h" +#endif // CONFIG_MEMCHECK static uint32_t cortexa8_cp15_c0_c1[8] = { 0x1031, 0x11, 0x400, 0, 0x31100003, 0x20000000, 0x01202000, 0x11 }; @@ -2668,3 +2671,12 @@ void HELPER(set_teecr)(CPUState *env, uint32_t val) } } +#ifdef CONFIG_MEMCHECK +void HELPER(on_call)(void* pc, void* ret) { + memcheck_on_call((target_ulong)pc, (target_ulong)ret); +} + +void HELPER(on_ret)(void* ret) { + memcheck_on_ret((target_ulong)ret); +} +#endif // CONFIG_MEMCHECK diff --git a/target-arm/helpers.h b/target-arm/helpers.h index abc54d2..a42b3ae 100644 --- a/target-arm/helpers.h +++ b/target-arm/helpers.h @@ -466,4 +466,19 @@ DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) DEF_HELPER_2(set_teecr, void, env, i32) +#ifdef CONFIG_MEMCHECK +/* Hooks to translated BL/BLX. This callback is used to build thread's + * calling stack. + * Param: + * First pointer contains guest PC where BL/BLX has been found. + * Second pointer contains guest PC where BL/BLX will return. + */ +DEF_HELPER_2(on_call, void, ptr, ptr) +/* Hooks to return from translated BL/BLX. This callback is used to build + * thread's calling stack. + * Param: + * Pointer contains guest PC where BL/BLX will return. + */ +DEF_HELPER_1(on_ret, void, ptr) +#endif // CONFIG_MEMCHECK #include "def-helper.h" diff --git a/target-arm/memcheck_arm_helpers.h b/target-arm/memcheck_arm_helpers.h new file mode 100644 index 0000000..a05668a --- /dev/null +++ b/target-arm/memcheck_arm_helpers.h @@ -0,0 +1,200 @@ +/* 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 memcheck helper routines used by ARM's translator. + */ + +#ifndef QEMU_TARGET_ARM_MEMCHECK_ARM_HELPERS_H +#define QEMU_TARGET_ARM_MEMCHECK_ARM_HELPERS_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 "helpers.h" +#include "memcheck/memcheck_api.h" + +/* Array of return addresses detected in gen_intermediate_code_internal. */ +AddrArray ret_addresses = { 0 }; + +/* Checks if call stack collection is enabled for the given context. + * We collect call stack only for the user mode (both, code and CPU), and on + * condition that memory checking, and call collection are enabled. It also + * seems that collecting stack for the linker code is excessive, as it doesn't + * provide much useful info for the memory checker. + * Return: + * boolean: 1 if stack collection is enabled for the given context, or 0 if + * it's not enabled. + */ +static inline int +watch_call_stack(DisasContext *s) +{ + if (!memcheck_enabled || !memcheck_watch_call_stack) { + return 0; + } + +#ifndef CONFIG_USER_ONLY + if (!s->user) { + /* We're not interested in kernel mode CPU stack. */ + return 0; + } +#endif // CONFIG_USER_ONLY + + /* We're not interested in kernel code stack (pc >= 0xC0000000). + * Android specific: We're also not interested in android linker stack + * (0xB0000000 - 0xB00FFFFF) */ + if (s->pc >= 0xC0000000 || (0xB0000000 <= s->pc && s->pc <= 0xB00FFFFF)) { + return 0; + } + return 1; +} + +/* Checks if given ARM instruction is BL, or BLX. + * Return: + * boolean: 1 if ARM instruction is BL/BLX, or 0 if it's not. + */ +static inline int +is_arm_bl_or_blx(uint32_t insn) +{ + /* ARM BL (immediate): xxxx 1011 xxxx xxxx xxxx xxxx xxxx xxxx + * ARM BLX (immediate): 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx + * ARM BLX (register): xxxx 0001 0010 xxxx xxxx xxxx 0011 xxxx + */ + if ((insn & 0x0F000000) == 0x0B000000 || // ARM BL (imm) + (insn & 0xFE000000) == 0xFA000000 || // ARM BLX (imm) + (insn & 0x0FF000F0) == 0x12000030) { // ARM BLX (reg) + return 1; + } + return 0; +} + +/* Checks if given THUMB instruction is BL, or BLX. + * Param: + * insn - THUMB instruction to check. + * ret_off - If insn is BL, or BLX, upon return ret_off contains + * instruction's byte size. If instruction is not BL, or BLX, content of + * this parameter is undefined on return. + * Return: + * boolean: 1 if THUMB instruction is BL/BLX, or 0 if it's not. + */ +static inline int +is_thumb_bl_or_blx(uint16_t insn, target_ulong* ret_off) +{ + /* THUMB BLX(register): 0100 0111 1xxx xxxx + * THUMB BL(1-stimmediate): 1111 0xxx xxxx xxxx + * THUMB BLX(1-stimmediate): 1111 0xxx xxxx xxxx + */ + if ((insn & 0xFF80) == 0x4780) { // THUMB BLX(reg) + *ret_off = 2; + return 1; + } else if ((insn & 0xF800) == 0xF000) { // THUMB BL(X)(imm) + *ret_off = 4; + return 1; + } + return 0; +} + +/* Registers a return address detected in gen_intermediate_code_internal. + * NOTE: If return address has been registered as new in this routine, this will + * cause invalidation of all existing TBs that contain translated code for that + * address. + * Param: + * env - CPU state environment. + * addr - Return address to register. + * Return: + * 1 - Address has been registered in this routine. + * -1 - Address has been already registered before. + * 0 - Insufficient memory. + */ +static int +register_ret_address(CPUState* env, target_ulong addr) +{ + int ret; + if ((0x90000000 <= addr && addr <= 0xBFFFFFFF)) { + /* Address belongs to a module that always loads at this fixed address. + * So, we can keep this address in the global array. */ + ret = addrarray_add(&ret_addresses, addr); + } else { + /* TODO: Figure out how to move "floating" part to the process + * descriptor. */ + ret = addrarray_add(&ret_addresses, addr); + } + assert(ret != 0); + + if (ret == 1) { + /* If this ret address has been added to the array, we need to make sure + * that all TBs that contain translated code for that address are + * invalidated. This will force retranslation of that code, which will + * make sure that our ret callback is set. This is also important part + * in keeping consistency between translated code, and intermediate code + * generated for guest PC calculation. If we don't invalidate TBs, and + * PC calculation code is generated, there will be inconsistency due to + * the fact that TB code doesn't contain ret callback, while PC calc + * code contains it. This inconsistency will lead to an immanent + * segmentation fault.*/ + TranslationBlock* tb; + const target_ulong phys_pc = get_phys_addr_code(env, addr); + const target_ulong phys_page1 = phys_pc & TARGET_PAGE_MASK; + + for(tb = tb_phys_hash[tb_phys_hash_func(phys_pc)]; tb != NULL; + tb = tb->phys_hash_next) { + if (tb->pc == addr && tb->page_addr[0] == phys_page1) { + tb_phys_invalidate(tb, -1); + } + } + } + return ret; +} + +/* Checks if given address is recognized as a return address. + * Return: + * boolean: 1 if if given address is recognized as a return address, + * or 0 if it's not. + */ +static inline int +is_ret_address(target_ulong addr) +{ + if ((0x90000000 <= addr && addr <= 0xBFFFFFFF)) { + return addrarray_check(&ret_addresses, addr); + } else { + return addrarray_check(&ret_addresses, addr); + } +} + +/* Adds "on_call" callback into generated intermediate code. */ +static inline void +set_on_call(target_ulong pc, target_ulong ret) +{ + TCGv_ptr tmp_pc = tcg_const_ptr(pc & ~1); + TCGv_ptr tmp_ret = tcg_const_ptr(ret & ~1); + + gen_helper_on_call(tmp_pc, tmp_ret); + + tcg_temp_free_ptr(tmp_ret); + tcg_temp_free_ptr(tmp_pc); +} + +/* Adds "on_ret" callback into generated intermediate code. */ +static inline void +set_on_ret(target_ulong ret) +{ + TCGv_ptr tmp_ret = tcg_const_ptr(ret & ~1); + + gen_helper_on_ret(tmp_ret); + + tcg_temp_free_ptr(tmp_ret); +} + +#endif // QEMU_TARGET_ARM_MEMCHECK_ARM_HELPERS_H diff --git a/target-arm/translate.c b/target-arm/translate.c index b6e1a34..4432c7b 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -65,6 +65,9 @@ typedef struct DisasContext { #if !defined(CONFIG_USER_ONLY) int user; #endif +#ifdef CONFIG_MEMCHECK + int search_pc; +#endif // CONFIG_MEMCHECK } DisasContext; #if defined(CONFIG_USER_ONLY) @@ -77,6 +80,26 @@ typedef struct DisasContext { #include "helpers.h" #endif /* CONFIG_TRACE */ +#ifdef CONFIG_MEMCHECK +/* + * Memchecker addition in this module is intended to inject qemu callback into + * translated code for each BL/BLX, as well as BL/BLX returns. These callbacks + * are used to build calling stack of the thread in order to provide better + * reporting on memory access violations. Although this may seem as something + * that may gratly impact the performance, in reality it doesn't. Overhead that + * is added by setting up callbacks and by callbacks themselves is neglectable. + * On the other hand, maintaining calling stack can indeed add some perf. + * overhead (TODO: provide solid numbers here). + * One of the things to watch out with regards to injecting callbacks, is + * consistency between intermediate code generated for execution, and for guest + * PC address calculation. If code doesn't match, a segmentation fault is + * guaranteed. + */ + +#include "memcheck/memcheck_proc_management.h" +#include "memcheck_arm_helpers.h" +#endif // CONFIG_MEMCHECK + /* These instructions trap after executing, so defer them until after the conditional executions state has been updated. */ #define DISAS_WFI 4 @@ -5783,8 +5806,22 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) TCGv tmp3; TCGv addr; TCGv_i64 tmp64; - insn = ldl_code(s->pc); + +#ifdef CONFIG_MEMCHECK + if (watch_call_stack(s)) { + if (is_ret_address(s->pc)) { + set_on_ret(s->pc); + } + if (is_arm_bl_or_blx(insn)) { + set_on_call(s->pc, s->pc + 4); + if (!s->search_pc) { + register_ret_address(env, s->pc + 4); + } + } + } +#endif // CONFIG_MEMCHECK + #ifdef CONFIG_TRACE if (tracing) { trace_add_insn(insn, 0); @@ -5792,6 +5829,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) gen_traceInsn(); } #endif + s->pc += 4; /* M variants do not implement ARM mode. */ @@ -6985,7 +7023,6 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) case 0xb: { int32_t offset; - /* branch (and link) */ val = (int32_t)s->pc; if (insn & (1 << 24)) { @@ -7170,9 +7207,11 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1) gen_traceTicks(ticks); } #endif - s->pc += 2; + insn |= (uint32_t)insn_hw1 << 16; + s->pc += 2; + if ((insn & 0xf800e800) != 0xf000e800) { ARCH(6T2); } @@ -8149,6 +8188,22 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s) } insn = lduw_code(s->pc); + +#ifdef CONFIG_MEMCHECK + if (watch_call_stack(s)) { + target_ulong ret_off; + if (is_ret_address(s->pc)) { + set_on_ret(s->pc); + } + if (is_thumb_bl_or_blx(insn, &ret_off)) { + set_on_call(s->pc, s->pc + ret_off); + if (!s->search_pc) { + register_ret_address(env, s->pc + ret_off); + } + } + } +#endif // CONFIG_MEMCHECK + #ifdef CONFIG_TRACE if (tracing) { int ticks = get_insn_ticks_thumb(insn); @@ -8834,6 +8889,9 @@ static inline void gen_intermediate_code_internal(CPUState *env, dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR; } #endif +#ifdef CONFIG_MEMCHECK + dc->search_pc = search_pc; +#endif // CONFIG_MEMCHECK cpu_F0s = tcg_temp_new_i32(); cpu_F1s = tcg_temp_new_i32(); cpu_F0d = tcg_temp_new_i64(); @@ -8892,7 +8950,15 @@ static inline void gen_intermediate_code_internal(CPUState *env, } } } + +#ifdef CONFIG_MEMCHECK + /* When memchecker is enabled, we need to keep a match between + * translated PC and guest PCs, so memchecker can quickly covert + * one to another. Note that we do that only for user mode. */ + if (search_pc || (memcheck_enabled && dc->user)) { +#else // CONFIG_MEMCHECK if (search_pc) { +#endif // CONFIG_MEMCHECK j = gen_opc_ptr - gen_opc_buf; if (lj < j) { lj++; @@ -9039,6 +9105,14 @@ done_generating: while (lj <= j) gen_opc_instr_start[lj++] = 0; } else { +#ifdef CONFIG_MEMCHECK + if (memcheck_enabled && dc->user) { + j = gen_opc_ptr - gen_opc_buf; + lj++; + while (lj <= j) + gen_opc_instr_start[lj++] = 0; + } +#endif // CONFIG_MEMCHECK tb->size = dc->pc - pc_start; tb->icount = num_insns; } @@ -57,7 +57,7 @@ #include "tcg-op.h" #include "elf.h" -static void patch_reloc(uint8_t *code_ptr, int type, +static void patch_reloc(uint8_t *code_ptr, int type, tcg_target_long value, tcg_target_long addend); static TCGOpDef tcg_op_defs[] = { @@ -75,6 +75,16 @@ static TCGRegSet tcg_target_call_clobber_regs; uint16_t *gen_opc_ptr; TCGArg *gen_opparam_ptr; +#ifdef CONFIG_MEMCHECK +/* + * Memchecker addition in this module is intended to build a map that matches + * translated PC to a guest PC. Map is built in tcg_gen_code_common routine, + * and is saved into temporary gen_opc_tpc2gpc_ptr array, that later will be + * copied into the TranslationBlock that represents the translated code. + */ +#include "memcheck/memcheck_api.h" +#endif // CONFIG_MEMCHECK + static inline void tcg_out8(TCGContext *s, uint8_t v) { *s->code_ptr++ = v; @@ -94,7 +104,7 @@ static inline void tcg_out32(TCGContext *s, uint32_t v) /* label relocation processing */ -void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, +void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, int label_index, long addend) { TCGLabel *l; @@ -103,7 +113,7 @@ void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, l = &s->labels[label_index]; if (l->has_value) { /* FIXME: This may break relocations on RISC targets that - modify instruction fields in place. The caller may not have + modify instruction fields in place. The caller may not have written the initial value. */ patch_reloc(code_ptr, type, l->u.value, addend); } else { @@ -117,7 +127,7 @@ void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, } } -static void tcg_out_label(TCGContext *s, int label_index, +static void tcg_out_label(TCGContext *s, int label_index, tcg_target_long value) { TCGLabel *l; @@ -157,7 +167,7 @@ void *tcg_malloc_internal(TCGContext *s, int size) { TCGPool *p; int pool_size; - + if (size > TCG_POOL_CHUNK_SIZE) { /* big malloc: insert a new pool (XXX: could optimize) */ p = qemu_malloc(sizeof(TCGPool) + size); @@ -180,7 +190,7 @@ void *tcg_malloc_internal(TCGContext *s, int size) p = qemu_malloc(sizeof(TCGPool) + pool_size); p->size = pool_size; p->next = NULL; - if (s->pool_current) + if (s->pool_current) s->pool_current->next = p; else s->pool_first = p; @@ -211,7 +221,7 @@ void tcg_context_init(TCGContext *s) memset(s, 0, sizeof(*s)); s->temps = s->static_temps; s->nb_globals = 0; - + /* Count total number of arguments and allocate the corresponding space */ total_args = 0; @@ -232,14 +242,14 @@ void tcg_context_init(TCGContext *s) sorted_args += n; args_ct += n; } - + tcg_target_init(s); /* init global prologue and epilogue */ s->code_buf = code_gen_prologue; s->code_ptr = s->code_buf; tcg_target_qemu_prologue(s); - flush_icache_range((unsigned long)s->code_buf, + flush_icache_range((unsigned long)s->code_buf, (unsigned long)s->code_ptr); } @@ -697,7 +707,7 @@ static char *tcg_get_arg_str_idx(TCGContext *s, char *buf, int buf_size, if (idx < s->nb_globals) { pstrcpy(buf, buf_size, ts->name); } else { - if (ts->temp_local) + if (ts->temp_local) snprintf(buf, buf_size, "loc%d", idx - s->nb_globals); else snprintf(buf, buf_size, "tmp%d", idx - s->nb_globals); @@ -735,7 +745,7 @@ static TCGHelperInfo *tcg_find_helper(TCGContext *s, tcg_target_ulong val) tcg_target_ulong v; if (unlikely(!s->helpers_sorted)) { - qsort(s->helpers, s->nb_helpers, sizeof(TCGHelperInfo), + qsort(s->helpers, s->nb_helpers, sizeof(TCGHelperInfo), helper_cmp); s->helpers_sorted = 1; } @@ -794,7 +804,7 @@ void tcg_dump_ops(TCGContext *s, FILE *outfile) #else pc = args[0]; #endif - if (!first_insn) + if (!first_insn) fprintf(outfile, "\n"); fprintf(outfile, " ---- 0x%" PRIx64, pc); first_insn = 0; @@ -834,7 +844,7 @@ void tcg_dump_ops(TCGContext *s, FILE *outfile) tcg_get_arg_str_idx(s, buf, sizeof(buf), args[nb_oargs + i])); } } - } else if (c == INDEX_op_movi_i32 + } else if (c == INDEX_op_movi_i32 #if TCG_TARGET_REG_BITS == 64 || c == INDEX_op_movi_i64 #endif @@ -845,7 +855,7 @@ void tcg_dump_ops(TCGContext *s, FILE *outfile) nb_oargs = def->nb_oargs; nb_iargs = def->nb_iargs; nb_cargs = def->nb_cargs; - fprintf(outfile, " %s %s,$", def->name, + fprintf(outfile, " %s %s,$", def->name, tcg_get_arg_str_idx(s, buf, sizeof(buf), args[0])); val = args[1]; th = tcg_find_helper(s, val); @@ -869,7 +879,7 @@ void tcg_dump_ops(TCGContext *s, FILE *outfile) nb_iargs = def->nb_iargs; nb_cargs = def->nb_cargs; } - + k = 0; for(i = 0; i < nb_oargs; i++) { if (k != 0) @@ -1026,7 +1036,7 @@ void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs) #ifdef USE_LIVENESS_ANALYSIS /* set a nop for an operation using 'nb_args' */ -static inline void tcg_set_nop(TCGContext *s, uint16_t *opc_ptr, +static inline void tcg_set_nop(TCGContext *s, uint16_t *opc_ptr, TCGArg *args, int nb_args) { if (nb_args == 0) { @@ -1090,7 +1100,7 @@ static void tcg_liveness_analysis(TCGContext *s) /* XXX: make it really dynamic */ s->op_dead_iargs = tcg_malloc(OPC_BUF_SIZE * sizeof(uint16_t)); - + dead_temps = tcg_malloc(s->nb_temps); memset(dead_temps, 1, s->nb_temps); @@ -1119,7 +1129,7 @@ static void tcg_liveness_analysis(TCGContext *s) if (!dead_temps[arg]) goto do_not_remove_call; } - tcg_set_nop(s, gen_opc_buf + op_index, + tcg_set_nop(s, gen_opc_buf + op_index, args - 1, nb_args); } else { do_not_remove_call: @@ -1129,7 +1139,7 @@ static void tcg_liveness_analysis(TCGContext *s) arg = args[i]; dead_temps[arg] = 1; } - + if (!(call_flags & TCG_CALL_CONST)) { /* globals are live (they may be used by the call) */ memset(dead_temps, 0, s->nb_globals); @@ -1269,8 +1279,8 @@ static void dump_regs(TCGContext *s) for(i = 0; i < TCG_TARGET_NB_REGS; i++) { if (s->reg_to_temp[i] >= 0) { - printf("%s: %s\n", - tcg_target_reg_names[i], + printf("%s: %s\n", + tcg_target_reg_names[i], tcg_get_arg_str_idx(s, buf, sizeof(buf), s->reg_to_temp[i])); } } @@ -1288,7 +1298,7 @@ static void check_regs(TCGContext *s) ts = &s->temps[k]; if (ts->val_type != TEMP_VAL_REG || ts->reg != reg) { - printf("Inconsistency for register %s:\n", + printf("Inconsistency for register %s:\n", tcg_target_reg_names[reg]); goto fail; } @@ -1299,7 +1309,7 @@ static void check_regs(TCGContext *s) if (ts->val_type == TEMP_VAL_REG && !ts->fixed_reg && s->reg_to_temp[ts->reg] != k) { - printf("Inconsistency for temp %s:\n", + printf("Inconsistency for temp %s:\n", tcg_get_arg_str_idx(s, buf, sizeof(buf), k)); fail: printf("reg state:\n"); @@ -1334,7 +1344,7 @@ static void tcg_reg_free(TCGContext *s, int reg) ts = &s->temps[temp]; assert(ts->val_type == TEMP_VAL_REG); if (!ts->mem_coherent) { - if (!ts->mem_allocated) + if (!ts->mem_allocated) temp_allocate_frame(s, temp); tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset); } @@ -1387,9 +1397,9 @@ static void temp_save(TCGContext *s, int temp, TCGRegSet allocated_regs) ts->val_type = TEMP_VAL_MEM; break; case TEMP_VAL_CONST: - reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], + reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], allocated_regs); - if (!ts->mem_allocated) + if (!ts->mem_allocated) temp_allocate_frame(s, temp); tcg_out_movi(s, ts->type, reg, ts->val); tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset); @@ -1519,7 +1529,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def, ots->mem_coherent = 0; } -static void tcg_reg_alloc_op(TCGContext *s, +static void tcg_reg_alloc_op(TCGContext *s, const TCGOpDef *def, int opc, const TCGArg *args, unsigned int dead_iargs) @@ -1536,11 +1546,11 @@ static void tcg_reg_alloc_op(TCGContext *s, nb_iargs = def->nb_iargs; /* copy constants */ - memcpy(new_args + nb_oargs + nb_iargs, - args + nb_oargs + nb_iargs, + memcpy(new_args + nb_oargs + nb_iargs, + args + nb_oargs + nb_iargs, sizeof(TCGArg) * def->nb_cargs); - /* satisfy input constraints */ + /* satisfy input constraints */ tcg_regset_set(allocated_regs, s->reserved_regs); for(k = 0; k < nb_iargs; k++) { i = def->sorted_args[nb_oargs + k]; @@ -1581,7 +1591,7 @@ static void tcg_reg_alloc_op(TCGContext *s, /* if the input is aliased to an output and if it is not dead after the instruction, we must allocate a new register and move it */ - if (!IS_DEAD_IARG(i - nb_oargs)) + if (!IS_DEAD_IARG(i - nb_oargs)) goto allocate_in_reg; } } @@ -1590,7 +1600,7 @@ static void tcg_reg_alloc_op(TCGContext *s, /* nothing to do : the constraint is satisfied */ } else { allocate_in_reg: - /* allocate a new register matching the constraint + /* allocate a new register matching the constraint and move the temporary register into it */ reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); tcg_out_mov(s, reg, ts->reg); @@ -1600,7 +1610,7 @@ static void tcg_reg_alloc_op(TCGContext *s, tcg_regset_set_reg(allocated_regs, reg); iarg_end: ; } - + if (def->flags & TCG_OPF_BB_END) { tcg_reg_alloc_bb_end(s, allocated_regs); } else { @@ -1616,9 +1626,9 @@ static void tcg_reg_alloc_op(TCGContext *s, } } } - + if (def->flags & TCG_OPF_CALL_CLOBBER) { - /* XXX: permit generic clobber register list ? */ + /* XXX: permit generic clobber register list ? */ for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) { tcg_reg_free(s, reg); @@ -1626,12 +1636,12 @@ static void tcg_reg_alloc_op(TCGContext *s, } /* XXX: for load/store we could do that only for the slow path (i.e. when a memory callback is called) */ - + /* store globals and free associated registers (we assume the insn can modify any global. */ save_globals(s, allocated_regs); } - + /* satisfy the output constraints */ tcg_regset_set(allocated_regs, s->reserved_regs); for(k = 0; k < nb_oargs; k++) { @@ -1659,7 +1669,7 @@ static void tcg_reg_alloc_op(TCGContext *s, ts->reg = reg; /* temp value is modified, so the value kept in memory is potentially not the same */ - ts->mem_coherent = 0; + ts->mem_coherent = 0; s->reg_to_temp[reg] = arg; } oarg_end: @@ -1669,7 +1679,7 @@ static void tcg_reg_alloc_op(TCGContext *s, /* emit instruction */ tcg_out_op(s, opc, new_args, const_args); - + /* move the outputs in the correct register if needed */ for(i = 0; i < nb_oargs; i++) { ts = &s->temps[args[i]]; @@ -1713,7 +1723,7 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, /* assign stack slots first */ /* XXX: preallocate call stack */ call_stack_size = (nb_params - nb_regs) * sizeof(tcg_target_long); - call_stack_size = (call_stack_size + TCG_TARGET_STACK_ALIGN - 1) & + call_stack_size = (call_stack_size + TCG_TARGET_STACK_ALIGN - 1) & ~(TCG_TARGET_STACK_ALIGN - 1); allocate_args = (call_stack_size > TCG_STATIC_CALL_ARGS_SIZE); if (allocate_args) { @@ -1731,13 +1741,13 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, if (ts->val_type == TEMP_VAL_REG) { tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset); } else if (ts->val_type == TEMP_VAL_MEM) { - reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], + reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], s->reserved_regs); /* XXX: not correct if reading values from the stack */ tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset); } else if (ts->val_type == TEMP_VAL_CONST) { - reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], + reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], s->reserved_regs); /* XXX: sign extend may be needed on some targets */ tcg_out_movi(s, ts->type, reg, ts->val); @@ -1750,7 +1760,7 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, stack_offset += sizeof(tcg_target_long); #endif } - + /* assign input registers */ tcg_regset_set(allocated_regs, s->reserved_regs); for(i = 0; i < nb_regs; i++) { @@ -1774,7 +1784,7 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, tcg_regset_set_reg(allocated_regs, reg); } } - + /* assign function address */ func_arg = args[nb_oargs + nb_iargs - 1]; arg_ct = &def->args_ct[0]; @@ -1807,8 +1817,8 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, } else { tcg_abort(); } - - + + /* mark dead temporaries and free the associated registers */ for(i = 0; i < nb_iargs; i++) { arg = args[nb_oargs + i]; @@ -1821,14 +1831,14 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, } } } - + /* clobber call registers */ for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) { tcg_reg_free(s, reg); } } - + /* store globals and free associated registers (we assume the call can modify any global. */ if (!(flags & TCG_CALL_CONST)) { @@ -1836,7 +1846,7 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, } tcg_out_op(s, opc, &func_arg, &const_func_arg); - + if (allocate_args) { tcg_out_addi(s, TCG_REG_CALL_STACK, STACK_DIR(call_stack_size)); } @@ -1856,11 +1866,11 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, s->reg_to_temp[ts->reg] = -1; ts->val_type = TEMP_VAL_REG; ts->reg = reg; - ts->mem_coherent = 0; + ts->mem_coherent = 0; s->reg_to_temp[reg] = arg; } } - + return nb_iargs + nb_oargs + def->nb_cargs + 1; } @@ -1888,6 +1898,9 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf, const TCGOpDef *def; unsigned int dead_iargs; const TCGArg *args; +#ifdef CONFIG_MEMCHECK + unsigned int tpc2gpc_index = 0; +#endif // CONFIG_MEMCHECK #ifdef DEBUG_DISAS if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { @@ -1921,7 +1934,27 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf, args = gen_opparam_buf; op_index = 0; +#ifdef CONFIG_MEMCHECK + gen_opc_tpc2gpc_pairs = 0; +#endif // CONFIG_MEMCHECK + for(;;) { +#ifdef CONFIG_MEMCHECK + /* On condition that memcheck is enabled, and operation index reached + * new operation in the guest code, save (pc_tb, pc_guest) pair into + * gen_opc_tpc2gpc array. Note that we do that only on condition that + * search_pc is < 0. This way we make sure that this is "normal" + * translation, called from tcg_gen_code, and not from + * tcg_gen_code_search_pc. */ + if (memcheck_enabled && search_pc < 0 && + gen_opc_instr_start[op_index]) { + gen_opc_tpc2gpc_ptr[tpc2gpc_index] = (target_ulong)s->code_ptr; + tpc2gpc_index++; + gen_opc_tpc2gpc_ptr[tpc2gpc_index] = gen_opc_pc[op_index]; + tpc2gpc_index++; + gen_opc_tpc2gpc_pairs++; + } +#endif // CONFIG_MEMCHECK opc = gen_opc_buf[op_index]; #ifdef CONFIG_PROFILER tcg_table_op_count[opc]++; @@ -2027,7 +2060,7 @@ int tcg_gen_code(TCGContext *s, uint8_t *gen_code_buf) tcg_gen_code_common(s, gen_code_buf, -1); /* flush instruction cache */ - flush_icache_range((unsigned long)gen_code_buf, + flush_icache_range((unsigned long)gen_code_buf, (unsigned long)s->code_ptr); return s->code_ptr - gen_code_buf; } @@ -2051,33 +2084,33 @@ void tcg_dump_info(FILE *f, tot = s->interm_time + s->code_time; cpu_fprintf(f, "JIT cycles %" PRId64 " (%0.3f s at 2.4 GHz)\n", tot, tot / 2.4e9); - cpu_fprintf(f, "translated TBs %" PRId64 " (aborted=%" PRId64 " %0.1f%%)\n", - s->tb_count, + cpu_fprintf(f, "translated TBs %" PRId64 " (aborted=%" PRId64 " %0.1f%%)\n", + s->tb_count, s->tb_count1 - s->tb_count, s->tb_count1 ? (double)(s->tb_count1 - s->tb_count) / s->tb_count1 * 100.0 : 0); - cpu_fprintf(f, "avg ops/TB %0.1f max=%d\n", + cpu_fprintf(f, "avg ops/TB %0.1f max=%d\n", s->tb_count ? (double)s->op_count / s->tb_count : 0, s->op_count_max); cpu_fprintf(f, "deleted ops/TB %0.2f\n", - s->tb_count ? + s->tb_count ? (double)s->del_op_count / s->tb_count : 0); cpu_fprintf(f, "avg temps/TB %0.2f max=%d\n", - s->tb_count ? + s->tb_count ? (double)s->temp_count / s->tb_count : 0, s->temp_count_max); - - cpu_fprintf(f, "cycles/op %0.1f\n", + + cpu_fprintf(f, "cycles/op %0.1f\n", s->op_count ? (double)tot / s->op_count : 0); - cpu_fprintf(f, "cycles/in byte %0.1f\n", + cpu_fprintf(f, "cycles/in byte %0.1f\n", s->code_in_len ? (double)tot / s->code_in_len : 0); - cpu_fprintf(f, "cycles/out byte %0.1f\n", + cpu_fprintf(f, "cycles/out byte %0.1f\n", s->code_out_len ? (double)tot / s->code_out_len : 0); if (tot == 0) tot = 1; - cpu_fprintf(f, " gen_interm time %0.1f%%\n", + cpu_fprintf(f, " gen_interm time %0.1f%%\n", (double)s->interm_time / tot * 100.0); - cpu_fprintf(f, " gen_code time %0.1f%%\n", + cpu_fprintf(f, " gen_code time %0.1f%%\n", (double)s->code_time / tot * 100.0); - cpu_fprintf(f, "liveness/code time %0.1f%%\n", + cpu_fprintf(f, "liveness/code time %0.1f%%\n", (double)s->la_time / (s->code_time ? s->code_time : 1) * 100.0); cpu_fprintf(f, "cpu_restore count %" PRId64 "\n", s->restore_count); diff --git a/translate-all.c b/translate-all.c index 4bdf2c9..8964758 100644 --- a/translate-all.c +++ b/translate-all.c @@ -49,6 +49,22 @@ target_ulong gen_opc_jump_pc[2]; uint32_t gen_opc_hflags[OPC_BUF_SIZE]; #endif +#ifdef CONFIG_MEMCHECK +/* + * Memchecker code in this module copies TB PC <-> Guest PC map to the TB + * descriptor after guest code has been translated in cpu_gen_init routine. + */ +#include "memcheck/memcheck_api.h" + +/* 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. */ +static target_ulong gen_opc_tpc2gpc[OPC_BUF_SIZE * 2]; +target_ulong* gen_opc_tpc2gpc_ptr = &gen_opc_tpc2gpc[0]; +/* Number of (tb_pc, guest_pc) pairs stored in gen_opc_tpc2gpc array. */ +unsigned int gen_opc_tpc2gpc_pairs; +#endif // CONFIG_MEMCHECK + /* XXX: suppress that */ unsigned long code_gen_max_block_size(void) { @@ -67,7 +83,7 @@ unsigned long code_gen_max_block_size(void) void cpu_gen_init(void) { - tcg_context_init(&tcg_ctx); + tcg_context_init(&tcg_ctx); tcg_set_frame(&tcg_ctx, TCG_AREG0, offsetof(CPUState, temp_buf), CPU_TEMP_BUF_NLONGS * sizeof(long)); } @@ -126,6 +142,19 @@ int cpu_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr) s->code_out_len += gen_code_size; #endif +#ifdef CONFIG_MEMCHECK + /* Save translated PC -> guest PC map into TB. */ + if (memcheck_enabled && gen_opc_tpc2gpc_pairs && is_cpu_user(env)) { + tb->tpc2gpc = + qemu_malloc(gen_opc_tpc2gpc_pairs * 2 * sizeof(target_ulong)); + if (tb->tpc2gpc != NULL) { + memcpy(tb->tpc2gpc, gen_opc_tpc2gpc_ptr, + gen_opc_tpc2gpc_pairs * 2 * sizeof(target_ulong)); + tb->tpc2gpc_pairs = gen_opc_tpc2gpc_pairs; + } + } +#endif // CONFIG_MEMCHECK + #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) { qemu_log("OUT: [size=%d]\n", *gen_code_size_ptr); |