From 2a2c39cdfa172cbac364069947a3b9e4fe84e8e7 Mon Sep 17 00:00:00 2001 From: Denis Khalikov Date: Thu, 5 Jul 2018 13:04:58 +0300 Subject: [PATCH 01/16] packaging: Add -marm and -fno-omit-frame-pointer To enable "fastunwinder" work on armv7l we should explicitly build libasan with -marm and -fno-omit-frame-pointer. Change-Id: I28e763fa7b088aa26a81e7bb6d3a7fa9ba7a714b --- gcc/testsuite/c-c++-common/asan/fast-unwind.c | 49 +++++++++++++++++++++++++++ packaging/gcc-aarch64.spec | 7 +++- packaging/gcc-armv7hl.spec | 7 +++- packaging/gcc-armv7l.spec | 7 +++- packaging/linaro-gcc.spec | 7 +++- 5 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/asan/fast-unwind.c diff --git a/gcc/testsuite/c-c++-common/asan/fast-unwind.c b/gcc/testsuite/c-c++-common/asan/fast-unwind.c new file mode 100644 index 0000000..f411a55 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/fast-unwind.c @@ -0,0 +1,49 @@ +/* { dg-do compile { target arm*-*-*} } */ +/* { dg-options "-fno-builtin-malloc -fno-builtin-free -fno-builtin-memset + * -fno-omit-frame-pointer -marm -Wa,-mimplicit-it=arm" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */ +/* { dg-shouldfail "asan" } */ + +#ifdef __cplusplus +extern "C" { +#endif + +void *memset (void *, int, __SIZE_TYPE__); +void *malloc (__SIZE_TYPE__); +void free (void *); + +const char * +__asan_default_options () { + return "fast_unwind_on_fatal=true"; +} + +#ifdef __cplusplus +} +#endif +volatile int ten = 10; +__attribute__ ((noinline)) int +foo () +{ + char *x = (char *) malloc (10); + memset(x, 0, 10); + int res = x[ten]; /* BOOOM */ + free(x); + return res; +} + +__attribute__ ((noinline)) int +bar () +{ + return foo (); +} + +int +main (int argc, char **argv) +{ + return bar (); +} + +/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+ thread T0.*(\n|\r\n|\r)" } */ +/* { dg-output " #0 0x\[0-9a-f\]+ +(in \[^\n\r]*foo\[^\n\r]*fast-unwind.c:29|\[(\]).*(\n|\r\n|\r)" } */ +/* { dg-output " #1 0x\[0-9a-f\]+ +(in \[^\n\r]*bar\[^\n\r]*fast-unwind.c:37|\[(\]).*(\n|\r\n|\r)" } */ +/* { dg-output " #2 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*fast-unwind.c:43|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */ diff --git a/packaging/gcc-aarch64.spec b/packaging/gcc-aarch64.spec index 7a34932..965c0e8 100644 --- a/packaging/gcc-aarch64.spec +++ b/packaging/gcc-aarch64.spec @@ -667,7 +667,12 @@ echo "" > gcc/DEV-PHASE %global gcc_release `sed -e 's/^.*-//g' %{_builddir}/gcc-%{version}/gcc/LINARO-VERSION` %build -%{?asan:%gcc_unforce_options} +%{?asan: +%gcc_unforce_options +%ifarch armv7l +RPM_OPT_FLAGS="$RPM_OPT_FLAGS -marm -Wa,-mimplicit-it=arm -fno-omit-frame-pointer" +%endif +} rm -rf obj mkdir obj cd obj diff --git a/packaging/gcc-armv7hl.spec b/packaging/gcc-armv7hl.spec index a5aef37..2c05eed 100644 --- a/packaging/gcc-armv7hl.spec +++ b/packaging/gcc-armv7hl.spec @@ -667,7 +667,12 @@ echo "" > gcc/DEV-PHASE %global gcc_release `sed -e 's/^.*-//g' %{_builddir}/gcc-%{version}/gcc/LINARO-VERSION` %build -%{?asan:%gcc_unforce_options} +%{?asan: +%gcc_unforce_options +%ifarch armv7l +RPM_OPT_FLAGS="$RPM_OPT_FLAGS -marm -Wa,-mimplicit-it=arm -fno-omit-frame-pointer" +%endif +} rm -rf obj mkdir obj cd obj diff --git a/packaging/gcc-armv7l.spec b/packaging/gcc-armv7l.spec index 74b06d2..638e853 100644 --- a/packaging/gcc-armv7l.spec +++ b/packaging/gcc-armv7l.spec @@ -667,7 +667,12 @@ echo "" > gcc/DEV-PHASE %global gcc_release `sed -e 's/^.*-//g' %{_builddir}/gcc-%{version}/gcc/LINARO-VERSION` %build -%{?asan:%gcc_unforce_options} +%{?asan: +%gcc_unforce_options +%ifarch armv7l +RPM_OPT_FLAGS="$RPM_OPT_FLAGS -marm -Wa,-mimplicit-it=arm -fno-omit-frame-pointer" +%endif +} rm -rf obj mkdir obj cd obj diff --git a/packaging/linaro-gcc.spec b/packaging/linaro-gcc.spec index 9de25fc..038f8e2 100644 --- a/packaging/linaro-gcc.spec +++ b/packaging/linaro-gcc.spec @@ -664,7 +664,12 @@ echo "" > gcc/DEV-PHASE %global gcc_release `sed -e 's/^.*-//g' %{_builddir}/gcc-%{version}/gcc/LINARO-VERSION` %build -%{?asan:%gcc_unforce_options} +%{?asan: +%gcc_unforce_options +%ifarch armv7l +RPM_OPT_FLAGS="$RPM_OPT_FLAGS -marm -Wa,-mimplicit-it=arm -fno-omit-frame-pointer" +%endif +} rm -rf obj mkdir obj cd obj -- 2.7.4 From 44b748e6c5d1b10696e9bfa3256f13ced23c4e5e Mon Sep 17 00:00:00 2001 From: Denis Khalikov Date: Wed, 24 Jan 2018 13:55:22 +0300 Subject: [PATCH 02/16] [ESan] EfficiencySanitizer implementation. EfficiencySanitizer (WorkingSet tool and CacheFragmentation tool) implementation for X86_64 and armv7l. The ESan is a set of compiler-based tools for analyzing targeted performance problems. This patch includes: 1. GCC pass. 1.1. GCC pass for WorkingSet tool. Special compiler pass instruments every memory access in the program. Memory accesses are simply prepended with a function call like __esan_aligned_load(addr), __esan_aligned_store(addr). 1.2. GCC pass for CacheFragmentation tool. Special compiler pass instruments every memory access to the struct field. Creates fields counter array, each cell of that array counts memory access to the special field. Creates array of struct, where every instance of the struct represetnts meta info of the real struct and so on. A call to __esan_init is inserted to the static constructor. A call to __esan_exit is inserted to the static destructor. 2. Runtime library. 2.1. WorkingSet tool. The runtime library simply manages shadow memory and computes statistic of the program efficiency. The tool maps one cashe line (64 bytes) of the program to the one byte of the shadow memory. Runtime library measures the data working set size of an application at each snapshot during execution. It can help understand phased behavior as well as providing basic direction for futher effort by the developer: e.g., knowing whether the working set is close to fitting in current L3 cashes or is many times larger can help to determine where to spend effort. 2.2. CacheFragmentation tool. The runtime part prints statistics about an amount of field accesses and detailed information about which field was actually accessed. 3. Testsuite. Simple testsuite. 4. HOW TO USE: 4.1. WorkingSet tool. To measure the working set size, you should build your binary or shared library with compile time flag -fsanitize=efficiency-working-set and set runtime options ESAN_OPTIONS=process_range_access=1:record_snapshots=1 4.2. CacheFragmentation tool. To enable CacheFragmentation tool you should compile your binary or shared library with compile time flag -fsanitize=efficiency-cache-frag and set runtime options ESAN_OPTIONS=build_mode=0:verbosity=1 Change-Id: Ie3231d3defa183712997e6fca39b1e8b4586e9b7 --- gcc/Makefile.in | 2 + gcc/builtins.def | 4 +- gcc/esan.c | 1177 +++++++++++++++++++++++ gcc/esan.h | 27 + gcc/flag-types.h | 2 + gcc/gcc.c | 19 + gcc/internal-fn.c | 8 + gcc/internal-fn.def | 1 + gcc/opts.c | 2 + gcc/passes.def | 3 + gcc/sanitizer.def | 49 + gcc/sanopt.c | 3 + gcc/testsuite/g++.dg/esan/cache-fragmentation.C | 96 ++ gcc/testsuite/g++.dg/esan/esan.exp | 34 + gcc/testsuite/g++.dg/esan/unalignet.C | 18 + gcc/testsuite/g++.dg/esan/workingset-simple.C | 29 + gcc/testsuite/lib/esan-dg.exp | 156 +++ gcc/toplev.c | 5 + gcc/tree-pass.h | 2 + gcc/tree-ssa-alias.c | 1 + libsanitizer/Makefile.am | 5 + libsanitizer/Makefile.in | 12 +- libsanitizer/asan/Makefile.in | 1 + libsanitizer/configure | 40 +- libsanitizer/configure.ac | 20 + libsanitizer/configure.tgt | 3 + libsanitizer/esan/Makefile.am | 78 ++ libsanitizer/esan/Makefile.in | 642 +++++++++++++ libsanitizer/esan/cache_frag.cpp | 216 +++++ libsanitizer/esan/cache_frag.h | 29 + libsanitizer/esan/esan.cpp | 313 ++++++ libsanitizer/esan/esan.h | 62 ++ libsanitizer/esan/esan.syms.extra | 4 + libsanitizer/esan/esan_circular_buffer.h | 96 ++ libsanitizer/esan/esan_flags.cpp | 68 ++ libsanitizer/esan/esan_flags.h | 41 + libsanitizer/esan/esan_flags.inc | 59 ++ libsanitizer/esan/esan_hashtable.h | 381 ++++++++ libsanitizer/esan/esan_interceptors.cpp | 568 +++++++++++ libsanitizer/esan/esan_interface.cpp | 122 +++ libsanitizer/esan/esan_interface_internal.h | 84 ++ libsanitizer/esan/esan_linux.cpp | 83 ++ libsanitizer/esan/esan_shadow.h | 318 ++++++ libsanitizer/esan/esan_sideline.h | 61 ++ libsanitizer/esan/esan_sideline_linux.cpp | 177 ++++ libsanitizer/esan/libtool-version | 6 + libsanitizer/esan/working_set.cpp | 281 ++++++ libsanitizer/esan/working_set.h | 40 + libsanitizer/esan/working_set_posix.cpp | 133 +++ libsanitizer/include/esan_interface.h | 50 + libsanitizer/interception/Makefile.in | 1 + libsanitizer/libbacktrace/Makefile.in | 1 + libsanitizer/libsanitizer.spec.in | 1 + libsanitizer/lsan/Makefile.in | 1 + libsanitizer/sanitizer_common/Makefile.in | 1 + libsanitizer/tsan/Makefile.in | 1 + libsanitizer/ubsan/Makefile.in | 1 + packaging/gcc-aarch64.spec | 25 +- packaging/gcc-armv7hl.spec | 25 +- packaging/gcc-armv7l.spec | 25 +- packaging/linaro-gcc.spec | 25 +- 61 files changed, 5728 insertions(+), 10 deletions(-) create mode 100644 gcc/esan.c create mode 100644 gcc/esan.h create mode 100644 gcc/testsuite/g++.dg/esan/cache-fragmentation.C create mode 100644 gcc/testsuite/g++.dg/esan/esan.exp create mode 100644 gcc/testsuite/g++.dg/esan/unalignet.C create mode 100644 gcc/testsuite/g++.dg/esan/workingset-simple.C create mode 100644 gcc/testsuite/lib/esan-dg.exp create mode 100644 libsanitizer/esan/Makefile.am create mode 100644 libsanitizer/esan/Makefile.in create mode 100644 libsanitizer/esan/cache_frag.cpp create mode 100644 libsanitizer/esan/cache_frag.h create mode 100644 libsanitizer/esan/esan.cpp create mode 100644 libsanitizer/esan/esan.h create mode 100644 libsanitizer/esan/esan.syms.extra create mode 100644 libsanitizer/esan/esan_circular_buffer.h create mode 100644 libsanitizer/esan/esan_flags.cpp create mode 100644 libsanitizer/esan/esan_flags.h create mode 100644 libsanitizer/esan/esan_flags.inc create mode 100644 libsanitizer/esan/esan_hashtable.h create mode 100644 libsanitizer/esan/esan_interceptors.cpp create mode 100644 libsanitizer/esan/esan_interface.cpp create mode 100644 libsanitizer/esan/esan_interface_internal.h create mode 100644 libsanitizer/esan/esan_linux.cpp create mode 100644 libsanitizer/esan/esan_shadow.h create mode 100644 libsanitizer/esan/esan_sideline.h create mode 100644 libsanitizer/esan/esan_sideline_linux.cpp create mode 100644 libsanitizer/esan/libtool-version create mode 100644 libsanitizer/esan/working_set.cpp create mode 100644 libsanitizer/esan/working_set.h create mode 100644 libsanitizer/esan/working_set_posix.cpp create mode 100644 libsanitizer/include/esan_interface.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 72afff1..ddb799f 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1436,6 +1436,7 @@ OBJS = \ tree-affine.o \ asan.o \ tsan.o \ + esan.o \ ubsan.o \ sanopt.o \ sancov.o \ @@ -2415,6 +2416,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/asan.c \ $(srcdir)/ubsan.c \ $(srcdir)/tsan.c \ + $(srcdir)/esan.c \ $(srcdir)/sanopt.c \ $(srcdir)/sancov.c \ $(srcdir)/ipa-devirt.c \ diff --git a/gcc/builtins.def b/gcc/builtins.def index 2fc7f65..10cbdbe 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -210,7 +210,9 @@ along with GCC; see the file COPYING3. If not see DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \ true, true, true, ATTRS, true, \ (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \ - | SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT) \ + | SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT \ + | SANITIZE_EFFICIENCY_WORKING_SET \ + | SANITIZE_EFFICIENCY_CACHE_FRAG) \ || flag_sanitize_coverage)) #undef DEF_CILKPLUS_BUILTIN diff --git a/gcc/esan.c b/gcc/esan.c new file mode 100644 index 0000000..312baa2 --- /dev/null +++ b/gcc/esan.c @@ -0,0 +1,1177 @@ +/* EfficiencySanitizer. + Copyright (C) 2011-2018 Free Software Foundation, Inc. + Contributed by Denis Khalikov. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC 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. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "rtl.h" +#include "tree.h" +#include "memmodel.h" +#include "gimple.h" +#include "tree-pass.h" +#include "cgraph.h" +#include "fold-const.h" +#include "gimplify.h" +#include "gimple-iterator.h" +#include "gimplify-me.h" +#include "tree-cfg.h" +#include "tree-iterator.h" +#include "esan.h" +#include "stringpool.h" +#include "attribs.h" +#include "builtins.h" +#include "asan.h" +#include "output.h" +#include "stor-layout.h" +#include "ssa.h" +#include "gimple-fold.h" + +/* The size taken from llvm part. */ +static const unsigned max_struct_field_counter_name_size = 512; +static char field_counter_name[max_struct_field_counter_name_size]; +static char counter_prefix[] = "struct."; + +/* Struct represents internal information about processed + record types. */ +struct esan_type +{ + esan_type () + : type (NULL_TREE), array_type (NULL_TREE), field_counter_name (NULL), + fields_count (0) + { + } + + esan_type (tree t, tree a) + : type (t), array_type (a), field_counter_name (NULL), fields_count (0) + { + } + + esan_type (const int value) + { + if (!value) + type = array_type = NULL_TREE; + } + + tree type; + tree array_type; + char *field_counter_name; + size_t fields_count; +}; + +/* Represents the type, field and index related to them. + Why do we need this type at all ? + + Assume we have following classes A, B, C as A <- B <- C, + and field access load %reg, &C->A_field, so we should + go recurively and find actual type A and field index + inside the A class. + This struct hepls to solve that problem. +*/ +struct esan_field_index +{ + esan_field_index () : type (NULL_TREE), field (NULL_TREE), index (0) {} + + esan_field_index (tree base_type, tree base_field, size_t base_index) + : type (base_type), field (base_field), index (base_index) + { + } + + esan_field_index (const esan_field_index &other) + { + type = other.type; + field = other.field; + index = other.index; + } + + esan_field_index & + operator= (const esan_field_index &other) + { + if (this == &other) + return *this; + + type = other.type; + field = other.field; + index = other.index; + return *this; + } + + tree type; + tree field; + size_t index; +}; + +/* FIXME: use hash_set instead vec + to improve search from O(n) to O(1). */ +static vec vec_esan_type; + +static tree +get_memory_access_decl (bool is_store, unsigned size) +{ + /* Choose an appropriate builtin. */ + enum built_in_function fcode; + if (size <= 1) + fcode + = is_store ? BUILT_IN_ESAN_ALIGNED_STORE1 : BUILT_IN_ESAN_ALIGNED_LOAD1; + else if (size <= 3) + fcode + = is_store ? BUILT_IN_ESAN_ALIGNED_STORE2 : BUILT_IN_ESAN_ALIGNED_LOAD2; + else if (size <= 7) + fcode + = is_store ? BUILT_IN_ESAN_ALIGNED_STORE4 : BUILT_IN_ESAN_ALIGNED_LOAD4; + else if (size <= 15) + fcode + = is_store ? BUILT_IN_ESAN_ALIGNED_STORE8 : BUILT_IN_ESAN_ALIGNED_LOAD8; + else + fcode + = is_store ? BUILT_IN_ESAN_ALIGNED_STORE16 : BUILT_IN_ESAN_ALIGNED_LOAD16; + return builtin_decl_implicit (fcode); +} + +static void +instrument_expr (gimple_stmt_iterator gsi, tree expr, bool is_store) +{ + tree base, expr_ptr; + basic_block bb; + HOST_WIDE_INT size; + gimple *stmt, *g; + gimple_seq seq; + location_t loc; + unsigned int align; + + size = int_size_in_bytes (TREE_TYPE (expr)); + /* Can't instrument memory accesses in case of size <= 0. */ + if (size <= 0) + return; + + HOST_WIDE_INT unused_bitsize, unused_bitpos; + tree offset; + machine_mode mode; + int unsignedp, reversep, volatilep = 0; + + base = get_inner_reference (expr, &unused_bitsize, &unused_bitpos, &offset, + &mode, &unsignedp, &reversep, &volatilep, false); + + if (TREE_READONLY (base) || (VAR_P (base) && DECL_HARD_REGISTER (base))) + return; + + stmt = gsi_stmt (gsi); + loc = gimple_location (stmt); + align = get_object_alignment (expr); + /* In this case we can't instrument memmory access. */ + if (align < BITS_PER_UNIT) + return; + + /* In this case we need folded ptr to expression. */ + expr_ptr = build_fold_addr_expr (unshare_expr (expr)); + + expr_ptr = force_gimple_operand (expr_ptr, &seq, true, NULL_TREE); + /* Build the esan's builtin. */ + g = gimple_build_call (get_memory_access_decl (is_store, size), 1, expr_ptr); + /* Set location. */ + gimple_set_location (g, loc); + gimple_seq_add_stmt_without_update (&seq, g); + + if (is_gimple_call (stmt) && is_store) + { + /* Could be a fallthrough edge. */ + if (is_ctrl_altering_stmt (stmt)) + { + edge e; + + bb = gsi_bb (gsi); + e = find_fallthru_edge (bb->succs); + if (e) + gsi_insert_seq_on_edge_immediate (e, seq); + } + else + /* Insert after the call on store. */ + gsi_insert_seq_after (&gsi, seq, GSI_NEW_STMT); + } + else + /* Insert before. */ + gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT); +} + +static void +maybe_instrument_working_set_gimple (gimple_stmt_iterator *gsi) +{ + gimple *stmt; + tree rhs, lhs; + + stmt = gsi_stmt (*gsi); + + if (is_gimple_call (stmt)) + return; + + if (is_gimple_assign (stmt) && !gimple_clobber_p (stmt)) + { + if (gimple_store_p (stmt)) + { + lhs = gimple_assign_lhs (stmt); + instrument_expr (*gsi, lhs, true); + } + if (gimple_assign_load_p (stmt)) + { + rhs = gimple_assign_rhs1 (stmt); + instrument_expr (*gsi, rhs, false); + } + } +} + +static size_t type_count; +static size_t default_count; + +/* Function generates unique id. */ +static size_t +gen_unique_id (char *buff, size_t *t_count) +{ + size_t size = 1; + size_t count = *t_count; + while (count) + { + *buff++ = '0' + count % 10; + count /= 10; + ++size; + } + *t_count += 1; + return size; +} + +static int +convert_to_type_id (char *buff, tree field_type) +{ + switch (TREE_CODE (field_type)) + { + case INTEGER_TYPE: + *buff = '1'; + return 1; + case REAL_TYPE: + *buff = '2'; + return 1; + case COMPLEX_TYPE: + *buff = '3'; + return 1; + case VECTOR_TYPE: + *buff = '4'; + return 1; + case ENUMERAL_TYPE: + *buff = '5'; + return 1; + case BOOLEAN_TYPE: + *buff = '6'; + return 1; + case POINTER_TYPE: + { + *buff++ = '8'; + return gen_unique_id (buff, &type_count); + } + default: + { + *buff++ = '9'; + return gen_unique_id (buff, &default_count); + } + } +} + +static void +reverse (char *buff, size_t len) +{ + if (!len) + return; + + size_t start, end; + + for (start = 0, end = len - 1; start < end; ++start, --end) + { + char temp = buff[end]; + buff[end] = buff[start]; + buff[start] = temp; + } +} + +static size_t +unique_id_size (size_t t_count) +{ + size_t size = 1; + while (t_count) + { + t_count /= 10; + ++size; + } + return size; +} + +static size_t +field_type_size (tree field) +{ + if (TREE_CODE (field) == FIELD_DECL) + { + switch (TREE_CODE (TREE_TYPE (field))) + { + case INTEGER_TYPE: + case REAL_TYPE: + case COMPLEX_TYPE: + case VECTOR_TYPE: + case ENUMERAL_TYPE: + case BOOLEAN_TYPE: + return 1; + case POINTER_TYPE: + { + return unique_id_size (type_count); + } + default: + { + return unique_id_size (default_count); + } + } + } + /* Skip nested types. */ + return 0; +} + +/* Functions creates struct field counter name consistent with + llvm part, because runtime relies on it. */ +static ssize_t +create_struct_field_counter_name (tree type, size_t *count) +{ + tree field; + size_t offset, start; + const char *type_name; + + /* Could be NULL type identifier. */ + if (!TYPE_IDENTIFIER (type)) + return -1; + + type_name = IDENTIFIER_POINTER (TYPE_IDENTIFIER (type)); + *count = offset = start = 0; + + memset (field_counter_name, 0, max_struct_field_counter_name_size); + + size_t counter_prefix_len = strlen (counter_prefix); + memcpy (field_counter_name, counter_prefix, counter_prefix_len); + offset += counter_prefix_len; + + size_t type_name_len = strlen (type_name); + + if (type_name_len + counter_prefix_len >= max_struct_field_counter_name_size) + return -1; + + memcpy (field_counter_name + offset, type_name, type_name_len); + offset += type_name_len; + + start = offset; + + for (field = TYPE_FIELDS (type); field + /* offset + sizeof (field) + sizeof ($). */ + && (offset + field_type_size (field) + 1) + < max_struct_field_counter_name_size; + field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + offset += convert_to_type_id (field_counter_name + offset, + TREE_TYPE (field)); + field_counter_name[offset++] = '$'; + /* Don't count nested types as a field. */ + if (TREE_CODE (TREE_TYPE (field)) != RECORD_TYPE) + ++*count; + } + } + + /* This is strangely, but llvm part writes fields id in reverse order. */ + reverse (field_counter_name + start, offset - start); + return offset; +} + +/* This should be kept consistent with LLVM's EfficiencySanitizer StructInfo. + struct StructInfo { + const char *StructName; + u32 Size; + u32 NumFields; + u32 *FieldOffset; // auxiliary struct field info. + u32 *FieldSize; // auxiliary struct field info. + const char **FieldTypeName; // auxiliary struct field info. + u64 *FieldCounters; + u64 *ArrayCounter; + }; */ + +/* Cached strcut_info_type tree. */ +static tree esan_struct_info_type; + +/* Returns struct_info_type tree. */ +static tree +esan_get_struct_info_type (void) +{ + /* Decription taken from llvm. */ + static const unsigned int count = 8; + static const char *field_names[count] + = {"StructName", "Size", "NumFields", "FieldOffset", + "FieldSize", "FieldTypeName", "FieldCounters", "ArrayCounters"}; + tree fields[count], ret; + + if (esan_struct_info_type) + return esan_struct_info_type; + + tree range_type = build_range_type (sizetype, size_zero_node, NULL_TREE); + /* StructName is a ptr to char with flex size. */ + tree flex_char_arr_type = build_array_type (char_type_node, range_type); + /* FieldOffset and FieldSize ptrs. */ + tree flex_uint_arr_type = build_array_type (unsigned_type_node, range_type); + /* FieldCounter and ArrayCounter. */ + tree flex_luint_arr_type + = build_array_type (long_long_unsigned_type_node, range_type); + + ret = make_node (RECORD_TYPE); + for (unsigned int i = 0; i < count; ++i) + { + switch (i) + { + case 0: + case 5: + fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier (field_names[i]), + build_pointer_type (flex_char_arr_type)); + break; + case 1: + case 2: + fields[i] + = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier (field_names[i]), unsigned_type_node); + break; + case 3: + case 4: + fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier (field_names[i]), + build_pointer_type (flex_uint_arr_type)); + break; + case 6: + case 7: + fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier (field_names[i]), + build_pointer_type (flex_luint_arr_type)); + break; + default: + break; + } + + DECL_CONTEXT (fields[i]) = ret; + if (i) + DECL_CHAIN (fields[i - 1]) = fields[i]; + } + + tree type_decl = build_decl (input_location, TYPE_DECL, + get_identifier ("__esan_strcut_type_info"), ret); + + DECL_IGNORED_P (type_decl) = 1; + DECL_ARTIFICIAL (type_decl) = 1; + TYPE_FIELDS (ret) = fields[0]; + TYPE_NAME (ret) = type_decl; + TYPE_STUB_DECL (ret) = type_decl; + layout_type (ret); + esan_struct_info_type = ret; + return ret; +} + +/* This should be kept consistent with LLVM's EfficiencySanitizer CacheFragInfo. + The tool-specific information per compilation unit (module). + struct CacheFragInfo { + const char *UnitName; + u32 NumStructs; + StructInfo *Structs; + }; */ + +static tree esan_cache_frag_info_type; + +static tree +esan_get_cache_frag_info_type (void) +{ + static const unsigned int count = 3; + static const char *field_names[count] = {"UnitName", "NumStructs", "Structs"}; + tree fields[count], ret; + + if (esan_cache_frag_info_type) + return esan_cache_frag_info_type; + + tree esan_struct_type_info = esan_get_struct_info_type (); + + tree range_type = build_range_type (sizetype, size_zero_node, NULL_TREE); + /* StructInfo Array Type. */ + tree struct_info_arr_type + = build_array_type (esan_struct_type_info, range_type); + + /* Unit Name. */ + tree flex_char_arr_type = build_array_type (char_type_node, range_type); + + ret = make_node (RECORD_TYPE); + + for (unsigned int i = 0; i < count; ++i) + { + switch (i) + { + case 0: + fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier (field_names[i]), + build_pointer_type (flex_char_arr_type)); + break; + case 1: + fields[i] + = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier (field_names[i]), unsigned_type_node); + break; + case 2: + fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier (field_names[i]), + build_pointer_type (struct_info_arr_type)); + break; + default: + break; + } + DECL_CONTEXT (fields[i]) = ret; + if (i) + DECL_CHAIN (fields[i - 1]) = fields[i]; + } + + tree type_decl + = build_decl (input_location, TYPE_DECL, + get_identifier ("__esan_cache_frag_info_type"), ret); + + DECL_IGNORED_P (type_decl) = 1; + DECL_ARTIFICIAL (type_decl) = 1; + TYPE_FIELDS (ret) = fields[0]; + TYPE_NAME (ret) = type_decl; + TYPE_STUB_DECL (ret) = type_decl; + layout_type (ret); + esan_cache_frag_info_type = ret; + return ret; +} + +static unsigned int esan_ids[2]; +static tree esan_array_counter_type; + +static tree +esan_get_array_counter_type (void) +{ + tree ret; + + if (esan_array_counter_type) + return esan_array_counter_type; + + tree range_type = build_range_type (sizetype, size_zero_node, NULL_TREE); + ret = build_array_type (long_long_unsigned_type_node, range_type); + + esan_array_counter_type = ret; + return ret; +} + +static void +esan_add_struct (tree array_addr, vec *v, + size_t fields_count, char *field_counter_name_type, + size_t struct_size) +{ + /* Create StructName. */ + const char *struct_name = field_counter_name_type; + size_t struct_name_len = strlen (struct_name) + 1; + tree struct_name_tree = build_string (struct_name_len, struct_name); + TREE_TYPE (struct_name_tree) + = build_array_type_nelts (char_type_node, struct_name_len); + TREE_READONLY (struct_name_tree) = 1; + TREE_STATIC (struct_name_tree) = 1; + + /* Create StructInfo type. */ + tree dtype = esan_get_struct_info_type (); + + /* Create an instance of StructInfo. */ + char tmp_name[32]; + ASM_GENERATE_INTERNAL_LABEL (tmp_name, "Lesan_type", esan_ids[0]++); + tree decl + = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier (tmp_name), dtype); + TREE_STATIC (decl) = 1; + TREE_PUBLIC (decl) = 0; + TREE_READONLY (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 1; + DECL_EXTERNAL (decl) = 0; + + /* Initialize created struct. */ + tree ctor = build_constructor_va ( + dtype, 8, NULL_TREE, build_fold_addr_expr (struct_name_tree), NULL_TREE, + build_int_cst (unsigned_type_node, struct_size), NULL_TREE, + build_int_cst (unsigned_type_node, fields_count), NULL_TREE, + build_zero_cst (long_long_unsigned_type_node), NULL_TREE, + build_zero_cst (long_long_unsigned_type_node), NULL_TREE, + build_zero_cst (long_long_unsigned_type_node), NULL_TREE, array_addr, + NULL_TREE, build_zero_cst (long_long_unsigned_type_node)); + + TREE_CONSTANT (ctor) = 1; + TREE_STATIC (ctor) = 1; + DECL_INITIAL (decl) = ctor; + CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, ctor); +} + +/* Go recursively and find the actual class type and field index inside it. */ +static esan_field_index +esan_get_field_index_internal (tree base_type, tree field, bool *found) +{ + tree field_it = TYPE_FIELDS (base_type); + esan_field_index field_index (base_type, field, 0); + + for (; field_it && !*found; field_it = DECL_CHAIN (field_it)) + { + if (TREE_CODE (field_it) == FIELD_DECL) + { + if (TREE_CODE (TREE_TYPE (field_it)) == RECORD_TYPE) + { + esan_field_index nested_field_index + = esan_get_field_index_internal (TREE_TYPE (field_it), field, + found); + if (*found) + return nested_field_index; + } + else if (field_it == field) + *found = true; + else + ++field_index.index; + } + } + return *found ? field_index : esan_field_index (NULL_TREE, NULL_TREE, 0); +} + +static bool +esan_get_field_index (tree base_type, tree field, esan_field_index *field_index) +{ + bool found = false; + /* At this momet we don't really know the record type where field is defined, + so can't cash the previous search. */ + *field_index = esan_get_field_index_internal (base_type, field, &found); + return found; +} + +static ssize_t +esan_vec_contains_type (tree type) +{ + ssize_t index = 0; + + if (type == NULL_TREE) + return -1; + + esan_type esan_type_instance; + /* This is O(n) cost, but I think we can improve it to ~ O(1) + by using a hash_set. */ + while (vec_esan_type.iterate (index, &esan_type_instance)) + { + if ((TYPE_IDENTIFIER (type)) == TYPE_IDENTIFIER (esan_type_instance.type)) + return index; + ++index; + } + return -1; +} + +bool +esan_expand_record_access_ifn (gimple_stmt_iterator *gsip) +{ + tree field; + size_t fields_count; + esan_field_index field_index_internal; + + tree base, base_type; + gimple_stmt_iterator gsi, gsi_origin; + gimple *stmt; + location_t loc; + + /* Expand internal ESAN_ACCESS. */ + gsi_origin = gsi = *gsip; + stmt = gsi_stmt (gsi); + loc = gimple_location (stmt); + base = gimple_call_arg (stmt, 0); + field = gimple_call_arg (stmt, 1); + esan_type esan_type_instance; + + tree array = NULL_TREE; + tree ptr = NULL_TREE; + + /* In case we packed pointer type into integer to allow some level of + optimization work properly. */ + base_type = TREE_CODE (base) == INTEGER_CST ? TREE_TYPE (TREE_TYPE (base)) + : TREE_TYPE (base); + + /* Don't forget to verify field index, before creating the fields counter + array. */ + if (!base_type + || !esan_get_field_index (base_type, field, &field_index_internal)) + { + unlink_stmt_vdef (stmt); + return gsi_remove (&gsi_origin, true); + } + + size_t field_index = field_index_internal.index; + base_type = TYPE_MAIN_VARIANT (field_index_internal.type); + + ssize_t index = esan_vec_contains_type (base_type); + + /* In case we have not found type in the pool of types + and field has valid index, we should + create fields counter array and add type to the pool of types. */ + if (index == -1) + { + fields_count = 0; + ssize_t field_counter_name_len + = create_struct_field_counter_name (base_type, &fields_count); + + if (field_counter_name_len == -1) + goto finish; + + char *field_counter_name_type + = (char *) xmalloc (field_counter_name_len + 1); + memset (field_counter_name_type, 0, field_counter_name_len + 1); + memcpy (field_counter_name_type, field_counter_name, + field_counter_name_len + 1); + + tree atype = esan_get_array_counter_type (); + array = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (field_counter_name), atype); + + TREE_STATIC (array) = 1; + TREE_PUBLIC (array) = 1; + DECL_ARTIFICIAL (array) = 1; + DECL_IGNORED_P (array) = 1; + DECL_WEAK (array) = 1; + DECL_SIZE (array) = build_int_cst ( + integer_type_node, + fields_count * tree_to_uhwi (TYPE_SIZE (long_long_unsigned_type_node))); + + DECL_SIZE_UNIT (array) + = build_int_cst (integer_type_node, + fields_count + * tree_to_uhwi ( + TYPE_SIZE_UNIT (long_long_unsigned_type_node))); + DECL_EXTERNAL (array) = 0; + + tree counter_ctor + = build_constructor_va (atype, 1, NULL_TREE, build_zero_cst (atype)); + TREE_CONSTANT (counter_ctor) = 1; + TREE_STATIC (counter_ctor) = 1; + DECL_INITIAL (array) = counter_ctor; + varpool_node::finalize_decl (array); + + ptr = build_fold_addr_expr (array); + esan_type_instance.type = base_type; + esan_type_instance.array_type = array; + esan_type_instance.field_counter_name = field_counter_name_type; + esan_type_instance.fields_count = fields_count; + vec_esan_type.safe_push (esan_type_instance); + } + /* We have already added the type into the pool, so fields counter + array was created. */ + else if (index >= 0) + { + if (vec_esan_type.iterate (index, &esan_type_instance)) + ptr = build_fold_addr_expr (esan_type_instance.array_type); + else + goto finish; + } + { + gimple *g; + /* Field number + sizeof (long int). */ + tree offset = build_int_cst (unsigned_type_node, + field_index + * tree_to_uhwi (TYPE_SIZE_UNIT ( + long_long_unsigned_type_node))); + /* array + offset. */ + g = gimple_build_assign (make_ssa_name (pointer_sized_int_node), PLUS_EXPR, + ptr, offset); + gimple_set_location (g, loc); + gsi_insert_before (gsip, g, GSI_SAME_STMT); + ptr = gimple_assign_lhs (g); + + tree call_decl = builtin_decl_implicit (BUILT_IN_ESAN_INCREMENT); + + g = gimple_build_call (call_decl, 1, ptr); + gimple_set_location (g, loc); + gsi_insert_before (gsip, g, GSI_SAME_STMT); + /* FIXME Inline __esan_increment for the better performance. */ + } +finish: + unlink_stmt_vdef (stmt); + return gsi_remove (&gsi_origin, true); +} + +static void +instrument_record_field_access (tree mem, tree base, gimple_stmt_iterator *iter, + tree field, const enum tree_code code) +{ + tree t; + gcall *g; + + t = TREE_OPERAND (base, 0); + + if ((code == MEM_REF && !POINTER_TYPE_P (TREE_TYPE (t))) || !field) + return; + + /* Don't instrument union accesses. */ + if ((TREE_CODE (TREE_TYPE (base)) != RECORD_TYPE) || mem == base) + return; + + /* Pack the pointer to integer, so other pass will keep it the same. */ + g = gimple_build_call_internal ( + IFN_ESAN_RECORD_ACCESS, 2, + (code == MEM_REF && POINTER_TYPE_P (TREE_TYPE (t))) + ? build_int_cst (build_pointer_type (TREE_TYPE (base)), 0) + : base, + field); + + gimple_set_location (g, gimple_location (gsi_stmt (*iter))); + gsi_insert_before (iter, g, GSI_SAME_STMT); +} + +static void +instrument_record_access (gimple_stmt_iterator *gsi, tree expr, bool is_store) +{ + tree field, t, base; + HOST_WIDE_INT unused_bitsize, unused_bitpos; + tree offset; + machine_mode mode; + int unsignedp, reversep, volatilep = 0; + + gimple *stmt = gsi_stmt (*gsi); + + HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (expr)); + + if (size <= 0) + return; + + tree base_tree + = get_inner_reference (expr, &unused_bitsize, &unused_bitpos, &offset, + &mode, &unsignedp, &reversep, &volatilep, false); + + if (TREE_READONLY (base_tree) + || (VAR_P (base_tree) && DECL_HARD_REGISTER (base_tree))) + return; + + if (get_object_alignment (expr) < BITS_PER_UNIT) + return; + + t = is_store ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt); + base = get_base_address (t); + field = NULL_TREE; + + /* Get the field. */ + if (TREE_CODE (t) == COMPONENT_REF) + field = TREE_OPERAND (t, 1); + + const enum tree_code code = TREE_CODE (base); + + if ((code == MEM_REF && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME) + || code == VAR_DECL) + instrument_record_field_access (t, base, gsi, field, code); +} + +static void +maybe_instrument_cache_frag_gimple (gimple_stmt_iterator *gsi) +{ + gimple *stmt; + tree lhs, rhs; + stmt = gsi_stmt (*gsi); + + if (is_gimple_call (stmt)) + return; + + if (is_gimple_assign (stmt) && !gimple_clobber_p (stmt)) + { + if (gimple_store_p (stmt)) + { + lhs = gimple_assign_lhs (stmt); + instrument_record_access (gsi, lhs, true); + } + else if (gimple_assign_load_p (stmt)) + { + rhs = gimple_assign_rhs1 (stmt); + instrument_record_access (gsi, rhs, false); + } + } +} + +static void +instrument_memory_accesses (void) +{ + basic_block bb; + gimple_stmt_iterator gsi; + + /* So, walk through all basic blocks + and try to instrument memory accesses. */ + FOR_EACH_BB_FN (bb, cfun) + { + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + if (flag_sanitize & SANITIZE_EFFICIENCY_WORKING_SET) + maybe_instrument_working_set_gimple (&gsi); + else if (flag_sanitize & SANITIZE_EFFICIENCY_CACHE_FRAG) + maybe_instrument_cache_frag_gimple (&gsi); + } + } +} + +/* EfficiencySanitizer instrumentation pass. */ + +static unsigned +esan_pass (void) +{ + initialize_sanitizer_builtins (); + instrument_memory_accesses (); + return 0; +} + +/* Creates an arrray of StructInfo structs and CacheInfo struct. + Inserts __esan_init () into the list of CTORs and + __esan_exit() into the listof DCTORs. */ + +void +esan_finish_file (void) +{ + tree ctor_statements = NULL_TREE; + tree dctor_statements = NULL_TREE; + initialize_sanitizer_builtins (); + tree tool_id = NULL_TREE; + + tree init_decl = builtin_decl_implicit (BUILT_IN_ESAN_INIT); + tree exit_decl = builtin_decl_implicit (BUILT_IN_ESAN_EXIT); + + if (flag_sanitize & SANITIZE_EFFICIENCY_CACHE_FRAG) + { + esan_type esan_type_instance; + size_t index = 0; + size_t num_types = vec_esan_type.length (); + vec *v; + vec_alloc (v, num_types); + + /* Create StructInfo struct for the every struct we have instrumented. */ + while (vec_esan_type.iterate (index, &esan_type_instance)) + { + tree ptr = build_fold_addr_expr (esan_type_instance.array_type); + esan_add_struct (ptr, v, esan_type_instance.fields_count, + esan_type_instance.field_counter_name, + int_size_in_bytes (esan_type_instance.type)); + + /* Was allocated by xmalloc, so free the memory. */ + free (esan_type_instance.field_counter_name); + ++index; + } + + /* Create module name string. */ + size_t module_name_len = strlen (main_input_filename) + 1; + tree module_name_str + = build_string (module_name_len, main_input_filename); + TREE_TYPE (module_name_str) + = build_array_type_nelts (char_type_node, module_name_len); + TREE_READONLY (module_name_str) = 1; + TREE_STATIC (module_name_str) = 1; + + tool_id = build_int_cst (unsigned_type_node, 1); + /* First, create the array of struct info type. */ + tree stype = esan_get_struct_info_type (); + + stype = build_array_type_nelts (stype, num_types); + + char tmp_info_name[32]; + ASM_GENERATE_INTERNAL_LABEL (tmp_info_name, "Lesan_info", 0); + + tree var = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (tmp_info_name), stype); + + TREE_STATIC (var) = 1; + TREE_PUBLIC (var) = 0; + TREE_READONLY (var) = 1; + DECL_ARTIFICIAL (var) = 1; + DECL_IGNORED_P (var) = 1; + DECL_EXTERNAL (var) = 0; + + tree ctor = build_constructor (stype, v); + + TREE_CONSTANT (ctor) = 1; + TREE_STATIC (ctor) = 1; + DECL_INITIAL (var) = ctor; + + varpool_node::finalize_decl (var); + + tree ctype = esan_get_cache_frag_info_type (); + char tmp_cache_name[32]; + ASM_GENERATE_INTERNAL_LABEL (tmp_cache_name, "Lesan_cache", 0); + + tree c_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (tmp_cache_name), ctype); + TREE_STATIC (c_var) = 1; + TREE_PUBLIC (c_var) = 0; + DECL_ARTIFICIAL (c_var) = 1; + DECL_IGNORED_P (c_var) = 1; + DECL_EXTERNAL (c_var) = 0; + + tree c_ctor + = build_constructor_va (ctype, 3, NULL_TREE, + build_fold_addr_expr (module_name_str), + NULL_TREE, + build_int_cst (unsigned_type_node, num_types), + NULL_TREE, build_fold_addr_expr (var)); + + TREE_CONSTANT (c_ctor) = 1; + TREE_STATIC (c_ctor) = 1; + DECL_INITIAL (c_var) = c_ctor; + varpool_node::finalize_decl (c_var); + + append_to_statement_list ( + build_call_expr (init_decl, 2, build_int_cst (unsigned_type_node, 1), + build_fold_addr_expr (c_var)), + &ctor_statements); + cgraph_build_static_cdtor ('I', ctor_statements, + MAX_RESERVED_INIT_PRIORITY - 1); + + append_to_statement_list (build_call_expr (exit_decl, 1, + build_fold_addr_expr (c_var)), + &dctor_statements); + cgraph_build_static_cdtor ('D', dctor_statements, + MAX_RESERVED_INIT_PRIORITY - 1); + } + else if (flag_sanitize & SANITIZE_EFFICIENCY_WORKING_SET) + { + tool_id = build_int_cst (unsigned_type_node, 2); + append_to_statement_list ( + build_call_expr (init_decl, 2, build_int_cst (unsigned_type_node, 2), + build_zero_cst (long_unsigned_type_node)), + &ctor_statements); + cgraph_build_static_cdtor ('I', ctor_statements, + MAX_RESERVED_INIT_PRIORITY - 1); + append_to_statement_list (build_call_expr (exit_decl, 0), + &dctor_statements); + cgraph_build_static_cdtor ('D', dctor_statements, + MAX_RESERVED_INIT_PRIORITY - 1); + } + + tree tool_id_type = build_array_type_nelts (unsigned_type_node, 1); + + /* Runtime part relies on __esan_which_tool global, which + informs the runtime about what part of runtime library should run. */ + tree tool_id_decl + = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier ("__esan_which_tool"), tool_id_type); + TREE_STATIC (tool_id_decl) = 1; + TREE_PUBLIC (tool_id_decl) = 1; + DECL_WEAK (tool_id_decl) = 1; + DECL_ARTIFICIAL (tool_id_decl) = 1; + DECL_IGNORED_P (tool_id_decl) = 1; + DECL_EXTERNAL (tool_id_decl) = 0; + + tree tool_id_ctor + = build_constructor_va (TREE_TYPE (tool_id_decl), 1, NULL_TREE, tool_id); + TREE_CONSTANT (tool_id_ctor) = 1; + TREE_STATIC (tool_id_ctor) = 1; + DECL_INITIAL (tool_id_decl) = tool_id_ctor; + varpool_node::finalize_decl (tool_id_decl); +} + +/* The pass descriptor. */ + +namespace { + +const pass_data pass_data_esan = { + GIMPLE_PASS, /* type */ + "esan", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + (PROP_ssa | PROP_cfg), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_update_ssa, /* todo_flags_finish */ +}; + +class pass_esan : public gimple_opt_pass +{ +public: + pass_esan (gcc::context *ctxt) + : gimple_opt_pass (pass_data_esan, ctxt) {} + + /* opt_pass methods: */ + opt_pass * + clone () { return new pass_esan (m_ctxt); } + + virtual bool + gate (function *) + { + return ( + (flag_sanitize + & (SANITIZE_EFFICIENCY_WORKING_SET | SANITIZE_EFFICIENCY_CACHE_FRAG)) + != 0); + } + + virtual unsigned int + execute (function *) { return esan_pass (); } + +}; // class pass_esan + +} // namespace + +gimple_opt_pass * +make_pass_esan (gcc::context *ctxt) +{ + return new pass_esan (ctxt); +} + +namespace { + +const pass_data pass_data_esan_O0 = { + GIMPLE_PASS, /* type */ + "esan0", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + (PROP_ssa | PROP_cfg), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_update_ssa, /* todo_flags_finish */ +}; + +class pass_esan_O0 : public gimple_opt_pass +{ +public: + pass_esan_O0 (gcc::context *ctxt) + : gimple_opt_pass (pass_data_esan_O0, ctxt) {} + + /* opt_pass methods: */ + virtual bool + gate (function *) + { + return ( + (flag_sanitize + & (SANITIZE_EFFICIENCY_WORKING_SET | SANITIZE_EFFICIENCY_CACHE_FRAG)) + != 0 + && !optimize); + } + + virtual unsigned int + execute (function *) { return esan_pass (); } + +}; // class pass_esan_O0 + +} // namespace + +gimple_opt_pass * +make_pass_esan_O0 (gcc::context *ctxt) +{ + return new pass_esan_O0 (ctxt); +} diff --git a/gcc/esan.h b/gcc/esan.h new file mode 100644 index 0000000..f483086 --- /dev/null +++ b/gcc/esan.h @@ -0,0 +1,27 @@ +/* EfficiencySanitizer. + Copyright (C) 2011-2018 Free Software Foundation, Inc. + Contributed by Denis Khalikov. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC 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. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef TREE_ESAN +#define TREE_ESAN + +extern void esan_finish_file (void); +extern bool esan_expand_record_access_ifn (gimple_stmt_iterator *gsip); + +#endif /* TREE_ESAN */ diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 18cf0a3..2a3a48e 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -254,6 +254,8 @@ enum sanitize_code { SANITIZE_VPTR = 1UL << 21, SANITIZE_BOUNDS_STRICT = 1UL << 22, SANITIZE_UI_OVERFLOW = 1 << 23, + SANITIZE_EFFICIENCY_WORKING_SET = 1UL << 24, + SANITIZE_EFFICIENCY_CACHE_FRAG = 1UL << 25, SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM diff --git a/gcc/gcc.c b/gcc/gcc.c index 1c2b3ab..ebd9d07 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -740,6 +740,19 @@ proper position among the other output files. */ #endif #endif +#ifndef LIBESAN_SPEC +#define STATIC_LIBESAN_LIBS \ + " %{static-libesan:%:include(libsanitizer.spec)%(link_libesan)}" +#ifdef HAVE_LD_STATIC_DYNAMIC +#define LIBESAN_SPEC "%{static-libesan:" LD_STATIC_OPTION \ + "} -lesan %{static-libesan:" LD_DYNAMIC_OPTION "}" \ + STATIC_LIBESAN_LIBS +#else +#define LIBESAN_SPEC "-lesan" STATIC_LIBESAN_LIBS +#endif +#endif + + /* Linker options for compressed debug sections. */ #if HAVE_LD_COMPRESS_DEBUG == 0 /* No linker support. */ @@ -977,6 +990,8 @@ proper position among the other output files. */ %{%:sanitize(thread):" LIBTSAN_SPEC "\ %{static:%ecannot specify -static with -fsanitize=thread}}\ %{!%:sanitize(address):%{%:sanitize(undefined):" LIBUBSAN_SPEC "}}\ + %{%:sanitize(efficiency-working-set):" LIBESAN_SPEC "}\ + %{%:sanitize(efficiency-cache-frag):" LIBESAN_SPEC "}\ %{%:sanitize(leak):" LIBLSAN_SPEC "}}}" #endif @@ -9304,6 +9319,10 @@ sanitize_spec_function (int argc, const char **argv) return ((flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_LEAK | SANITIZE_THREAD)) == SANITIZE_LEAK) ? "" : NULL; + if (strcmp (argv[0], "efficiency-working-set") == 0) + return (flag_sanitize & SANITIZE_EFFICIENCY_WORKING_SET) ? "" : NULL; + if (strcmp (argv[0], "efficiency-cache-frag") == 0) + return (flag_sanitize & SANITIZE_EFFICIENCY_CACHE_FRAG) ? "" : NULL; return NULL; } diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c index 1523a91..ab87b87 100644 --- a/gcc/internal-fn.c +++ b/gcc/internal-fn.c @@ -229,6 +229,14 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *) /* This should get expanded in the sanopt pass. */ static void +expand_ESAN_RECORD_ACCESS (internal_fn, gcall *) +{ + gcc_unreachable (); +} + +/* This should get expanded in the sanopt pass. */ + +static void expand_ASAN_CHECK (internal_fn, gcall *) { gcc_unreachable (); diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def index 761576d7..c35c11e 100644 --- a/gcc/internal-fn.def +++ b/gcc/internal-fn.def @@ -166,6 +166,7 @@ DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (TSAN_FUNC_EXIT, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (VA_ARG, ECF_NOTHROW | ECF_LEAF, NULL) +DEF_INTERNAL_FN (ESAN_RECORD_ACCESS, ECF_LEAF | ECF_NOTHROW, NULL) /* An unduplicable, uncombinable function. Generally used to preserve a CFG property in the face of jump threading, tail merging or diff --git a/gcc/opts.c b/gcc/opts.c index 99542cb..c36db60 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1466,6 +1466,8 @@ const struct sanitizer_opts_s sanitizer_opts[] = SANITIZER_OPT (returns-nonnull-attribute, SANITIZE_RETURNS_NONNULL_ATTRIBUTE), SANITIZER_OPT (object-size, SANITIZE_OBJECT_SIZE), SANITIZER_OPT (vptr, SANITIZE_VPTR), + SANITIZER_OPT (efficiency-working-set, SANITIZE_EFFICIENCY_WORKING_SET), + SANITIZER_OPT (efficiency-cache-frag, SANITIZE_EFFICIENCY_CACHE_FRAG), SANITIZER_OPT (all, ~0), #undef SANITIZER_OPT { NULL, 0, 0 } diff --git a/gcc/passes.def b/gcc/passes.def index 7aed144..792e6fa 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -250,6 +250,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_sancov); NEXT_PASS (pass_asan); NEXT_PASS (pass_tsan); + NEXT_PASS (pass_esan); /* Pass group that runs when 1) enabled, 2) there are loops in the function. Make sure to run pass_fix_loops before to discover/remove loops before running the gate function @@ -354,6 +355,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_sancov); NEXT_PASS (pass_asan); NEXT_PASS (pass_tsan); + NEXT_PASS (pass_esan); /* ??? We do want some kind of loop invariant motion, but we possibly need to adjust LIM to be more friendly towards preserving accurate debug information here. */ @@ -378,6 +380,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_sancov_O0); NEXT_PASS (pass_asan_O0); NEXT_PASS (pass_tsan_O0); + NEXT_PASS (pass_esan_O0); NEXT_PASS (pass_sanopt); NEXT_PASS (pass_cleanup_eh); NEXT_PASS (pass_lower_resx); diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def index b229d5c..7f0bf8c 100644 --- a/gcc/sanitizer.def +++ b/gcc/sanitizer.def @@ -166,6 +166,55 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT, "__asan_after_dynamic_init", BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) +/* Efficiency Sanitizer */ +DEF_SANITIZER_BUILTIN(BUILT_IN_ESAN_INIT, "__esan_init", + BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_ESAN_EXIT, "__esan_exit", + BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_ALIGNED_STORE1, "__esan_aligned_store1", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_ALIGNED_LOAD1, "__esan_aligned_load1", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_ALIGNED_STORE2, "__esan_aligned_store2", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_ALIGNED_LOAD2, "__esan_aligned_load2", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_ALIGNED_STORE4, "__esan_aligned_store4", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_ALIGNED_LOAD4, "__esan_aligned_load4", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_ALIGNED_STORE8, "__esan_aligned_store8", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_ALIGNED_LOAD8, "__esan_aligned_load8", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_ALIGNED_STORE16, "__esan_aligned_store16", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_ALIGNED_LOAD16, "__esan_aligned_load16", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_UNALIGNED_STORE1, "__esan_unaligned_store1", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_UNALIGNED_LOAD1, "__esan_unaligned_load1", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_UNALIGNED_STORE2, "__esan_unaligned_store2", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_UNALIGNED_LOAD2, "__esan_unaligned_load2", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_UNALIGNED_STORE4, "__esan_unaligned_store4", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_UNALIGNED_LOAD4, "__esan_unaligned_load4", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_UNALIGNED_STORE8, "__esan_unaligned_store8", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_UNALIGNED_LOAD8, "__esan_unaligned_load8", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_UNALIGNED_STORE16, "__esan_unaligned_store16", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_UNALIGNED_LOAD16, "__esan_unaligned_load16", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN (BUILT_IN_ESAN_INCREMENT, "__esan_increment", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) + + /* Thread Sanitizer */ DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) diff --git a/gcc/sanopt.c b/gcc/sanopt.c index 2660453..e864360 100644 --- a/gcc/sanopt.c +++ b/gcc/sanopt.c @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple-iterator.h" #include "asan.h" #include "ubsan.h" +#include "esan.h" #include "params.h" #include "tree-hash-traits.h" @@ -704,6 +705,8 @@ pass_sanopt::execute (function *fun) case IFN_ASAN_CHECK: no_next = asan_expand_check_ifn (&gsi, use_calls); break; + case IFN_ESAN_RECORD_ACCESS: + no_next = esan_expand_record_access_ifn (&gsi); default: break; } diff --git a/gcc/testsuite/g++.dg/esan/cache-fragmentation.C b/gcc/testsuite/g++.dg/esan/cache-fragmentation.C new file mode 100644 index 0000000..e95c564 --- /dev/null +++ b/gcc/testsuite/g++.dg/esan/cache-fragmentation.C @@ -0,0 +1,96 @@ +// { dg-do run { target { x86_64-*-linux* } } } +// { dg-options "-fsanitize=efficiency-cache-frag" } + +struct A +{ + int a; + char b; + float c; + double d; + unsigned e; + unsigned long f; + unsigned long long j; +}; + +struct B : A +{ + int f1; + int f2; + int f3; +}; + +struct C : B +{ + int f4; + int f5; + int f6; +}; + +struct E +{ + int f7; + int f8; +}; + +struct D : B, E +{ + int f9; +}; + +static int count = 10; + +static void +foo () +{ + A a; + for (int i = 0; i < count; ++i) + { + a.a = i; + a.b = a.a; + a.c = i; + a.d = i; + a.e = i; + a.f = i; + a.j = i; + } + + A b; + A *ptr = &b; + for (int i = 0; i < count; ++i) + { + ptr->a = i; + ptr->b = ptr->a; + ptr->c = i; + ptr->d = i; + ptr->e = i; + ptr->f = i; + ptr->j = i; + } +} + +void +bar () +{ + C c; + for (int i = 0; i < count; ++i) + { + c.a = i; + c.f1 = i; + c.f4 = i; + } + + D d; + for (int i = 0; i < count; ++i) + { + d.f9 = i; + d.f7 = i; + } +} + +int +main (int argc, char **argv) +{ + foo (); + bar (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/esan/esan.exp b/gcc/testsuite/g++.dg/esan/esan.exp new file mode 100644 index 0000000..9052347 --- /dev/null +++ b/gcc/testsuite/g++.dg/esan/esan.exp @@ -0,0 +1,34 @@ +# Copyright (C) 2012-2016 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC 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. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +# Load support procs. +load_lib g++-dg.exp +load_lib esan-dg.exp + +# Initialize `dg'. +dg-init +esan_init + +# Main loop. +if [check_effective_target_fsanitize_esan] { + gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] "" "" +} + +# All done. +esan_finish +dg-finish diff --git a/gcc/testsuite/g++.dg/esan/unalignet.C b/gcc/testsuite/g++.dg/esan/unalignet.C new file mode 100644 index 0000000..cc15683 --- /dev/null +++ b/gcc/testsuite/g++.dg/esan/unalignet.C @@ -0,0 +1,18 @@ +// { dg-do run { target { x86_64-*-linux* } } } +// { dg-options "-fsanitize=efficiency-working-set" } + +class A { +public: + bool : 1; + bool IsExpandedByDefault : 1; +}; + +void fn1() { + A a; + a.IsExpandedByDefault = 0; +} + +int main (int argc, char **argv) { + fn1 (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/esan/workingset-simple.C b/gcc/testsuite/g++.dg/esan/workingset-simple.C new file mode 100644 index 0000000..f38134e --- /dev/null +++ b/gcc/testsuite/g++.dg/esan/workingset-simple.C @@ -0,0 +1,29 @@ +// { dg-do run { target { x86_64-*-linux* } } } +// { dg-options "-fsanitize=efficiency-working-set" } + +#include +#include +#include +#include + +const int size = 0x1 << 25; // 520k cache lines +const int line_size = 64; + +int main(int argc, char **argv) { + char *bufA = (char *)malloc(sizeof(char) * line_size); + char bufB[64]; + char *bufC = (char *)mmap(0, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + bufA[0] = 0; + // This additional access to the same line should not increase the line + // count: but it's difficult to make a non-flaky test that measures the + // lines down to the ones digit so right now we're not really testing that. + // If we add a heap-only mode we may be able to be more precise. + bufA[1] = 0; + bufB[33] = 1; + for (int i = 0; i < size; i += line_size) + bufC[i] = 0; + free(bufA); + munmap(bufC, 0x4000); + return 0; +} diff --git a/gcc/testsuite/lib/esan-dg.exp b/gcc/testsuite/lib/esan-dg.exp new file mode 100644 index 0000000..e3b933d --- /dev/null +++ b/gcc/testsuite/lib/esan-dg.exp @@ -0,0 +1,156 @@ +# Copyright (C) 2013-2016 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +# Return 1 if compilation with -fsanitize=efficiency-working-set is error-free for trivial +# code, 0 otherwise. Also set what to do by default here, depending on the +# result of a runtime test. + +proc check_effective_target_fsanitize_esan {} { + global individual_timeout + global dg-do-what-default + + if ![check_no_compiler_messages fsanitize_esan executable { + int main (void) { return 0; } + }] { + return 0 + } + + # Lower timeout value in case test does not terminate properly. + set individual_timeout 20 + if [check_runtime_nocache esan_works { + int main () { return 0; } + }] { + set dg-do-what-default run + } else { + set dg-do-what-default link + } + unset individual_timeout + + return 1 +} + +# +# esan_link_flags -- compute library path and flags to find libesan. +# (originally from g++.exp) +# + +proc esan_link_flags { paths } { + global srcdir + global ld_library_path + global shlib_ext + global esan_saved_library_path + + set gccpath ${paths} + set flags "" + + set shlib_ext [get_shlib_extension] + set esan_saved_library_path $ld_library_path + + if { $gccpath != "" } { + if { [file exists "${gccpath}/libsanitizer/esan/.libs/libesan.a"] + || [file exists "${gccpath}/libsanitizer/esan/.libs/libesan.${shlib_ext}"] } { + append flags " -B${gccpath}/libsanitizer/esan/ " + append flags " -L${gccpath}/libsanitizer/esan/.libs " + append ld_library_path ":${gccpath}/libsanitizer/esan/.libs" + } + } else { + global tool_root_dir + + set libesan [lookfor_file ${tool_root_dir} libesan] + if { $libesan != "" } { + append flags "-L${libesan} " + append ld_library_path ":${libesan}" + } + } + + set_ld_library_path_env_vars + + return "$flags" +} + +# +# esan_init -- called at the start of each subdir of tests +# + +proc esan_init { args } { + global TEST_ALWAYS_FLAGS + global ALWAYS_CXXFLAGS + global TOOL_OPTIONS + global esan_saved_TEST_ALWAYS_FLAGS + global esan_saved_ALWAYS_CXXFLAGS + global dg-do-what-default + global esan_saved_dg-do-what-default + + set link_flags "" + if ![is_remote host] { + if [info exists TOOL_OPTIONS] { + set link_flags "[esan_link_flags [get_multilibs ${TOOL_OPTIONS}]]" + } else { + set link_flags "[esan_link_flags [get_multilibs]]" + } + } + + if [info exists dg-do-what-default] { + set esan_saved_dg-do-what-default ${dg-do-what-default} + } + if [info exists TEST_ALWAYS_FLAGS] { + set esan_saved_TEST_ALWAYS_FLAGS $TEST_ALWAYS_FLAGS + } + if [info exists ALWAYS_CXXFLAGS] { + set esan_saved_ALWAYS_CXXFLAGS $ALWAYS_CXXFLAGS + set ALWAYS_CXXFLAGS [concat "{ldflags=$link_flags}" $ALWAYS_CXXFLAGS] + set ALWAYS_CXXFLAGS [concat "{additional_flags=-fsanitize=efficiency-working-set -g}" $ALWAYS_CXXFLAGS] + } else { + if [info exists TEST_ALWAYS_FLAGS] { + set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=efficiency-working-set -g $TEST_ALWAYS_FLAGS" + } else { + set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=efficiency-working-set -g" + } + } +} + +# +# esan_finish -- called at the end of each subdir of tests +# + +proc esan_finish { args } { + global TEST_ALWAYS_FLAGS + global esan_saved_TEST_ALWAYS_FLAGS + global esan_saved_ALWAYS_CXXFLAGS + global dg-do-what-default + global esan_saved_dg-do-what-default + global esan_saved_library_path + global ld_library_path + + if [info exists esan_saved_ALWAYS_CXXFLAGS ] { + set ALWAYS_CXXFLAGS $esan_saved_ALWAYS_CXXFLAGS + } else { + if [info exists esan_saved_TEST_ALWAYS_FLAGS] { + set TEST_ALWAYS_FLAGS $esan_saved_TEST_ALWAYS_FLAGS + } else { + unset TEST_ALWAYS_FLAGS + } + } + + if [info exists esan_saved_dg-do-what-default] { + set dg-do-what-default ${esan_saved_dg-do-what-default} + } else { + unset dg-do-what-default + } + set ld_library_path $esan_saved_library_path + set_ld_library_path_env_vars + clear_effective_target_cache +} diff --git a/gcc/toplev.c b/gcc/toplev.c index 3e0b54a..827840b 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -64,6 +64,7 @@ along with GCC; see the file COPYING3. If not see #include "opts-diagnostic.h" #include "asan.h" #include "tsan.h" +#include "esan.h" #include "plugin.h" #include "context.h" #include "pass_manager.h" @@ -519,6 +520,10 @@ compile_file (void) if (flag_sanitize & SANITIZE_THREAD) tsan_finish_file (); + if (flag_sanitize + & (SANITIZE_EFFICIENCY_WORKING_SET | SANITIZE_EFFICIENCY_CACHE_FRAG)) + esan_finish_file (); + if (flag_check_pointer_bounds) chkp_finish_file (); diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 5f5055d..17b3170 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -351,6 +351,8 @@ extern gimple_opt_pass *make_pass_asan (gcc::context *ctxt); extern gimple_opt_pass *make_pass_asan_O0 (gcc::context *ctxt); extern gimple_opt_pass *make_pass_tsan (gcc::context *ctxt); extern gimple_opt_pass *make_pass_tsan_O0 (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_esan (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_esan_O0 (gcc::context *ctxt); extern gimple_opt_pass *make_pass_sancov (gcc::context *ctxt); extern gimple_opt_pass *make_pass_sancov_O0 (gcc::context *ctxt); extern gimple_opt_pass *make_pass_lower_cf (gcc::context *ctxt); diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c index 08f10e5..532e9c6 100644 --- a/gcc/tree-ssa-alias.c +++ b/gcc/tree-ssa-alias.c @@ -1885,6 +1885,7 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref) case IFN_UBSAN_VPTR: case IFN_UBSAN_OBJECT_SIZE: case IFN_ASAN_CHECK: + case IFN_ESAN_RECORD_ACCESS: return false; default: break; diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am index 72c9a29..ec7eaf0 100644 --- a/libsanitizer/Makefile.am +++ b/libsanitizer/Makefile.am @@ -23,6 +23,11 @@ SUBDIRS += tsan nodist_saninclude_HEADERS += \ include/sanitizer/tsan_interface_atomic.h endif +if ESAN_SUPPORTED +SUBDIRS += esan +nodist_saninclude_HEADERS += \ + include/sanitizer/esan_interface.h +endif endif ## May be used by toolexeclibdir. diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in index 4ce3647..7d8a735 100644 --- a/libsanitizer/Makefile.in +++ b/libsanitizer/Makefile.in @@ -61,6 +61,10 @@ target_triplet = @target@ @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_5 = \ @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@ include/sanitizer/tsan_interface_atomic.h +@ESAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_6 = esan +@ESAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_7 = \ +@ESAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@ include/sanitizer/esan_interface.h + subdir = . DIST_COMMON = ChangeLog $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/configure $(am__configure_deps) \ @@ -142,7 +146,7 @@ AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ ETAGS = etags CTAGS = ctags DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan ubsan \ - asan tsan + asan tsan esan ACLOCAL = @ACLOCAL@ ALLOC_FILE = @ALLOC_FILE@ AMTAR = @AMTAR@ @@ -260,6 +264,7 @@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ link_libasan = @link_libasan@ +link_libesan = @link_libesan@ link_liblsan = @link_liblsan@ link_libtsan = @link_libtsan@ link_libubsan = @link_libubsan@ @@ -290,10 +295,11 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ACLOCAL_AMFLAGS = -I .. -I ../config sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer -nodist_saninclude_HEADERS = $(am__append_1) $(am__append_5) +nodist_saninclude_HEADERS = $(am__append_1) $(am__append_5) \ + $(am__append_7) @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \ @SANITIZER_SUPPORTED_TRUE@ $(am__append_3) lsan ubsan asan \ -@SANITIZER_SUPPORTED_TRUE@ $(am__append_4) +@SANITIZER_SUPPORTED_TRUE@ $(am__append_4) $(am__append_6) gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER) # Work around what appears to be a GNU make bug handling MAKEFLAGS diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in index 5af30c4..7704e24 100644 --- a/libsanitizer/asan/Makefile.in +++ b/libsanitizer/asan/Makefile.in @@ -268,6 +268,7 @@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ link_libasan = @link_libasan@ +link_libesan = @link_libesan@ link_liblsan = @link_liblsan@ link_libtsan = @link_libtsan@ link_libubsan = @link_libubsan@ diff --git a/libsanitizer/configure b/libsanitizer/configure index 3d859ac..2861330 100755 --- a/libsanitizer/configure +++ b/libsanitizer/configure @@ -620,10 +620,13 @@ SANITIZER_SUPPORTED_FALSE SANITIZER_SUPPORTED_TRUE USING_MAC_INTERPOSE_FALSE USING_MAC_INTERPOSE_TRUE +link_libesan link_liblsan link_libubsan link_libtsan link_libasan +ESAN_SUPPORTED_FALSE +ESAN_SUPPORTED_TRUE LSAN_SUPPORTED_FALSE LSAN_SUPPORTED_TRUE TSAN_SUPPORTED_FALSE @@ -12029,7 +12032,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 12032 "configure" +#line 12035 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -12135,7 +12138,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 12138 "configure" +#line 12141 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -15482,6 +15485,7 @@ fi # Get target configury. unset TSAN_SUPPORTED unset LSAN_SUPPORTED +unset ESAN_SUPPORTED . ${srcdir}/configure.tgt if test "x$TSAN_SUPPORTED" = "xyes"; then TSAN_SUPPORTED_TRUE= @@ -15499,6 +15503,14 @@ else LSAN_SUPPORTED_FALSE= fi + if test "x$ESAN_SUPPORTED" = "xyes"; then + ESAN_SUPPORTED_TRUE= + ESAN_SUPPORTED_FALSE='#' +else + ESAN_SUPPORTED_TRUE='#' + ESAN_SUPPORTED_FALSE= +fi + # Check for functions needed. for ac_func in clock_getres clock_gettime clock_settime @@ -15620,6 +15632,9 @@ link_libubsan=$link_sanitizer_common link_liblsan=$link_sanitizer_common +# Set up the set of additional libraries that we need to link against for libesan. +link_libesan=$link_sanitizer_common + # At least for glibc, clock_gettime is in librt. But don't pull that # in if it still doesn't give us the function we want. This @@ -16531,6 +16546,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then fi +if test "x$ESAN_SUPPORTED" = "xyes"; then + ac_config_files="$ac_config_files esan/Makefile" + +fi + @@ -16667,6 +16687,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then as_fn_error "conditional \"LSAN_SUPPORTED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${ESAN_SUPPORTED_TRUE}" && test -z "${ESAN_SUPPORTED_FALSE}"; then + as_fn_error "conditional \"ESAN_SUPPORTED\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then as_fn_error "conditional \"USING_MAC_INTERPOSE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -17679,6 +17703,7 @@ do "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;; "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;; "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;; + "esan/Makefile") CONFIG_FILES="$CONFIG_FILES esan/Makefile" ;; *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac @@ -19625,6 +19650,17 @@ _EOF . ${multi_basedir}/config-ml.in { ml_norecursion=; unset ml_norecursion;} ;; + "esan/Makefile":F) cat > vpsed$$ << \_EOF +s!`test -f '$<' || echo '$(srcdir)/'`!! +_EOF + sed -f vpsed$$ $ac_file > tmp$$ + mv tmp$$ $ac_file + rm vpsed$$ + echo 'MULTISUBDIR =' >> $ac_file + ml_norecursion=yes + . ${multi_basedir}/config-ml.in + { ml_norecursion=; unset ml_norecursion;} + ;; esac done # for ac_tag diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac index a6f0639..c8629a8 100644 --- a/libsanitizer/configure.ac +++ b/libsanitizer/configure.ac @@ -88,9 +88,11 @@ fi # Get target configury. unset TSAN_SUPPORTED unset LSAN_SUPPORTED +unset ESAN_SUPPORTED . ${srcdir}/configure.tgt AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"]) AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"]) +AM_CONDITIONAL(ESAN_SUPPORTED, [test "x$ESAN_SUPPORTED" = "xyes"]) # Check for functions needed. AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime) @@ -124,6 +126,9 @@ AC_SUBST(link_libubsan) link_liblsan=$link_sanitizer_common AC_SUBST(link_liblsan) +# Set up the set of additional libraries that we need to link against for libesan. +link_libesan=$link_sanitizer_common +AC_SUBST(link_libesan) # At least for glibc, clock_gettime is in librt. But don't pull that # in if it still doesn't give us the function we want. This @@ -398,6 +403,21 @@ _EOF ]) fi +if test "x$ESAN_SUPPORTED" = "xyes"; then + AC_CONFIG_FILES(AC_FOREACH([DIR], [esan], [DIR/Makefile ]), + [cat > vpsed$$ << \_EOF +s!`test -f '$<' || echo '$(srcdir)/'`!! +_EOF + sed -f vpsed$$ $ac_file > tmp$$ + mv tmp$$ $ac_file + rm vpsed$$ + echo 'MULTISUBDIR =' >> $ac_file + ml_norecursion=yes + . ${multi_basedir}/config-ml.in + AS_UNSET([ml_norecursion]) +]) +fi + AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS]) AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS]) diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt index 9a6b158..2d99312 100644 --- a/libsanitizer/configure.tgt +++ b/libsanitizer/configure.tgt @@ -25,6 +25,7 @@ case "${target}" in x86_64-*-linux* | i?86-*-linux*) if test x$ac_cv_sizeof_void_p = x8; then TSAN_SUPPORTED=yes + ESAN_SUPPORTED=yes TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_amd64.lo SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS=sanitizer_linux_x86_64.lo fi @@ -37,6 +38,7 @@ case "${target}" in ;; arm*-*-linux*) LSAN_SUPPORTED=yes + ESAN_SUPPORTED=yes ;; aarch64*-*-linux*) if test x$ac_cv_sizeof_void_p = x8; then @@ -47,6 +49,7 @@ case "${target}" in ;; x86_64-*-darwin[1]* | i?86-*-darwin[1]*) TSAN_SUPPORTED=no + ESAN_SUPPORTED=no ;; *) UNSUPPORTED=1 diff --git a/libsanitizer/esan/Makefile.am b/libsanitizer/esan/Makefile.am new file mode 100644 index 0000000..9264b9b --- /dev/null +++ b/libsanitizer/esan/Makefile.am @@ -0,0 +1,78 @@ +AM_CPPFLAGS = -I $(top_srcdir) -I $(top_srcdir)/include + +# May be used by toolexeclibdir. +gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER) + +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros +AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS) +AM_CXXFLAGS += -std=gnu++11 +AM_CXXFLAGS += $(EXTRA_CXXFLAGS) +ACLOCAL_AMFLAGS = -I m4 + +toolexeclib_LTLIBRARIES = libesan.la + +esan_files = \ + esan.cpp \ + esan_flags.cpp \ + esan_interface.cpp \ + esan_interceptors.cpp \ + esan_linux.cpp \ + esan_sideline_linux.cpp \ + cache_frag.cpp \ + working_set.cpp \ + working_set_posix.cpp + +libesan_la_SOURCES = $(esan_files) +libesan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la +if !USING_MAC_INTERPOSE +libesan_la_LIBADD += $(top_builddir)/interception/libinterception.la +endif +if LIBBACKTRACE_SUPPORTED +libesan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la +endif +libesan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS) +libesan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libesan) + +# Work around what appears to be a GNU make bug handling MAKEFLAGS +# values defined in terms of make variables, as is the case for CC and +# friends when we are called from the top level Makefile. +AM_MAKEFLAGS = \ + "AR_FLAGS=$(AR_FLAGS)" \ + "CC_FOR_BUILD=$(CC_FOR_BUILD)" \ + "CFLAGS=$(CFLAGS)" \ + "CXXFLAGS=$(CXXFLAGS)" \ + "CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \ + "CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \ + "INSTALL=$(INSTALL)" \ + "INSTALL_DATA=$(INSTALL_DATA)" \ + "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \ + "INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \ + "JC1FLAGS=$(JC1FLAGS)" \ + "LDFLAGS=$(LDFLAGS)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \ + "MAKE=$(MAKE)" \ + "MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \ + "PICFLAG=$(PICFLAG)" \ + "PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \ + "SHELL=$(SHELL)" \ + "RUNTESTFLAGS=$(RUNTESTFLAGS)" \ + "exec_prefix=$(exec_prefix)" \ + "infodir=$(infodir)" \ + "libdir=$(libdir)" \ + "prefix=$(prefix)" \ + "includedir=$(includedir)" \ + "AR=$(AR)" \ + "AS=$(AS)" \ + "LD=$(LD)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "NM=$(NM)" \ + "PICFLAG=$(PICFLAG)" \ + "RANLIB=$(RANLIB)" \ + "DESTDIR=$(DESTDIR)" + +MAKEOVERRIDES= + +## ################################################################ + diff --git a/libsanitizer/esan/Makefile.in b/libsanitizer/esan/Makefile.in new file mode 100644 index 0000000..80eec72 --- /dev/null +++ b/libsanitizer/esan/Makefile.in @@ -0,0 +1,642 @@ +# Makefile.in generated by automake 1.11.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@USING_MAC_INTERPOSE_FALSE@am__append_1 = $(top_builddir)/interception/libinterception.la +@LIBBACKTRACE_SUPPORTED_TRUE@am__append_2 = $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la +subdir = esan +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \ + $(top_srcdir)/../config/depstand.m4 \ + $(top_srcdir)/../config/lead-dot.m4 \ + $(top_srcdir)/../config/libstdc++-raw-cxx.m4 \ + $(top_srcdir)/../config/multi.m4 \ + $(top_srcdir)/../config/override.m4 \ + $(top_srcdir)/../config/stdint.m4 \ + $(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \ + $(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/../libtool.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(toolexeclibdir)" +LTLIBRARIES = $(toolexeclib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libesan_la_DEPENDENCIES = \ + $(top_builddir)/sanitizer_common/libsanitizer_common.la \ + $(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1) +am__objects_1 = esan.lo esan_flags.lo esan_interface.lo \ + esan_interceptors.lo esan_linux.lo esan_sideline_linux.lo \ + cache_frag.lo working_set.lo working_set_posix.lo +am_libesan_la_OBJECTS = $(am__objects_1) +libesan_la_OBJECTS = $(am_libesan_la_OBJECTS) +libesan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(libesan_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/../depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libesan_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +ETAGS = etags +CTAGS = ctags +ACLOCAL = @ACLOCAL@ +ALLOC_FILE = @ALLOC_FILE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BACKTRACE_SUPPORTED = @BACKTRACE_SUPPORTED@ +BACKTRACE_SUPPORTS_THREADS = @BACKTRACE_SUPPORTS_THREADS@ +BACKTRACE_USES_MALLOC = @BACKTRACE_USES_MALLOC@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FORMAT_FILE = @FORMAT_FILE@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX_RAW_CXX_CXXFLAGS = @LIBSTDCXX_RAW_CXX_CXXFLAGS@ +LIBSTDCXX_RAW_CXX_LDFLAGS = @LIBSTDCXX_RAW_CXX_LDFLAGS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OBSTACK_DEFS = @OBSTACK_DEFS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPC_DEFS = @RPC_DEFS@ +SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@ +VERSION = @VERSION@ +VIEW_FILE = @VIEW_FILE@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +enable_shared = @enable_shared@ +enable_static = @enable_static@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +link_libasan = @link_libasan@ +link_libesan = @link_libesan@ +link_liblsan = @link_liblsan@ +link_libtsan = @link_libtsan@ +link_libubsan = @link_libubsan@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +multi_basedir = @multi_basedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_noncanonical = @target_noncanonical@ +target_os = @target_os@ +target_vendor = @target_vendor@ +toolexecdir = @toolexecdir@ +toolexeclibdir = @toolexeclibdir@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -I $(top_srcdir) -I $(top_srcdir)/include + +# May be used by toolexeclibdir. +gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER) +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \ + -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \ + -fomit-frame-pointer -funwind-tables -fvisibility=hidden \ + -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) \ + -std=gnu++11 $(EXTRA_CXXFLAGS) +ACLOCAL_AMFLAGS = -I m4 +toolexeclib_LTLIBRARIES = libesan.la +esan_files = \ + esan.cpp \ + esan_flags.cpp \ + esan_interface.cpp \ + esan_interceptors.cpp \ + esan_linux.cpp \ + esan_sideline_linux.cpp \ + cache_frag.cpp \ + working_set.cpp \ + working_set_posix.cpp + +libesan_la_SOURCES = $(esan_files) +libesan_la_LIBADD = \ + $(top_builddir)/sanitizer_common/libsanitizer_common.la \ + $(am__append_1) $(am__append_2) $(LIBSTDCXX_RAW_CXX_LDFLAGS) +libesan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libesan) + +# Work around what appears to be a GNU make bug handling MAKEFLAGS +# values defined in terms of make variables, as is the case for CC and +# friends when we are called from the top level Makefile. +AM_MAKEFLAGS = \ + "AR_FLAGS=$(AR_FLAGS)" \ + "CC_FOR_BUILD=$(CC_FOR_BUILD)" \ + "CFLAGS=$(CFLAGS)" \ + "CXXFLAGS=$(CXXFLAGS)" \ + "CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \ + "CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \ + "INSTALL=$(INSTALL)" \ + "INSTALL_DATA=$(INSTALL_DATA)" \ + "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \ + "INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \ + "JC1FLAGS=$(JC1FLAGS)" \ + "LDFLAGS=$(LDFLAGS)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \ + "MAKE=$(MAKE)" \ + "MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \ + "PICFLAG=$(PICFLAG)" \ + "PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \ + "SHELL=$(SHELL)" \ + "RUNTESTFLAGS=$(RUNTESTFLAGS)" \ + "exec_prefix=$(exec_prefix)" \ + "infodir=$(infodir)" \ + "libdir=$(libdir)" \ + "prefix=$(prefix)" \ + "includedir=$(includedir)" \ + "AR=$(AR)" \ + "AS=$(AS)" \ + "LD=$(LD)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "NM=$(NM)" \ + "PICFLAG=$(PICFLAG)" \ + "RANLIB=$(RANLIB)" \ + "DESTDIR=$(DESTDIR)" + +MAKEOVERRIDES = +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign esan/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign esan/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-toolexeclibLTLIBRARIES: $(toolexeclib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(toolexeclibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(toolexeclibdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(toolexeclibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(toolexeclibdir)"; \ + } + +uninstall-toolexeclibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(toolexeclibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(toolexeclibdir)/$$f"; \ + done + +clean-toolexeclibLTLIBRARIES: + -test -z "$(toolexeclib_LTLIBRARIES)" || rm -f $(toolexeclib_LTLIBRARIES) + @list='$(toolexeclib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libesan.la: $(libesan_la_OBJECTS) $(libesan_la_DEPENDENCIES) $(EXTRA_libesan_la_DEPENDENCIES) + $(libesan_la_LINK) -rpath $(toolexeclibdir) $(libesan_la_OBJECTS) $(libesan_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache_frag.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/esan.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/esan_flags.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/esan_interceptors.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/esan_interface.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/esan_linux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/esan_sideline_linux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/working_set.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/working_set_posix.Plo@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(toolexeclibdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-toolexeclibLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-toolexeclibLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-toolexeclibLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags dvi dvi-am html html-am info info-am install \ + install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip install-toolexeclibLTLIBRARIES installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-toolexeclibLTLIBRARIES + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/libsanitizer/esan/cache_frag.cpp b/libsanitizer/esan/cache_frag.cpp new file mode 100644 index 0000000..47fb638 --- /dev/null +++ b/libsanitizer/esan/cache_frag.cpp @@ -0,0 +1,216 @@ +//===-- cache_frag.cpp ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// This file contains cache fragmentation-specific code. +//===----------------------------------------------------------------------===// + +#include "esan.h" +#include "esan_flags.h" +#include "sanitizer_common/sanitizer_addrhashmap.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include + +namespace __esan { + +//===-- Struct field access counter runtime -------------------------------===// + +// This should be kept consistent with LLVM's EfficiencySanitizer StructInfo. +struct StructInfo { + const char *StructName; + u32 Size; + u32 NumFields; + u32 *FieldOffset; // auxiliary struct field info. + u32 *FieldSize; // auxiliary struct field info. + const char **FieldTypeName; // auxiliary struct field info. + u64 *FieldCounters; + u64 *ArrayCounter; + bool hasAuxFieldInfo() { return FieldOffset != nullptr; } +}; + +// This should be kept consistent with LLVM's EfficiencySanitizer CacheFragInfo. +// The tool-specific information per compilation unit (module). +struct CacheFragInfo { + const char *UnitName; + u32 NumStructs; + StructInfo *Structs; +}; + +struct StructCounter { + StructInfo *Struct; + u64 Count; // The total access count of the struct. + u64 Ratio; // Difference ratio for the struct layout access. +}; + +// We use StructHashMap to keep track of an unique copy of StructCounter. +typedef AddrHashMap StructHashMap; +struct Context { + StructHashMap StructMap; + u32 NumStructs; + u64 TotalCount; // The total access count of all structs. +}; +static Context *Ctx; + +static void reportStructSummary() { + // FIXME: provide a better struct field access summary report. + Report("%s: total struct field access count = %llu\n", SanitizerToolName, + Ctx->TotalCount); +} + +// FIXME: we are still exploring proper ways to evaluate the difference between +// struct field counts. Currently, we use a simple formula to calculate the +// difference ratio: V1/V2. +static inline u64 computeDifferenceRatio(u64 Val1, u64 Val2) { + if (Val2 > Val1) { + Swap(Val1, Val2); + } + if (Val2 == 0) + Val2 = 1; + return (Val1 / Val2); +} + +static void reportStructCounter(StructHashMap::Handle &Handle) { + const u32 TypePrintLimit = 512; + const char *type, *start, *end; + StructInfo *Struct = Handle->Struct; + // Union field address calculation is done via bitcast instead of GEP, + // so the count for union is always 0. + // We skip the union report to avoid confusion. + if (strncmp(Struct->StructName, "union.", 6) == 0) + return; + // Remove the '.' after class/struct during print. + if (strncmp(Struct->StructName, "class.", 6) == 0) { + type = "class"; + start = &Struct->StructName[6]; + } else { + type = "struct"; + start = &Struct->StructName[7]; + } + // Remove the suffixes with '$' during print. + end = strchr(start, '$'); + if (end == nullptr) + return; + + Report(" %s %.*s\n", type, end - start, start); + Report(" size = %u, count = %llu, ratio = %llu\n", Struct->Size, + Handle->Count, Handle->Ratio); + if (Struct->hasAuxFieldInfo()) { + for (u32 i = 0; i < Struct->NumFields; ++i) { + Report(" #%2u: offset = %u,\t size = %u," + "\t count = %llu,\t type = %.*s\n", + i, Struct->FieldOffset[i], Struct->FieldSize[i], + Struct->FieldCounters[i], TypePrintLimit, Struct->FieldTypeName[i]); + } + } else { + for (u32 i = 0; i < Struct->NumFields; ++i) { + Report(" #%2u: count = %llu\n", i, Struct->FieldCounters[i]); + } + } +} + +static void computeStructRatio(StructHashMap::Handle &Handle) { + Handle->Ratio = 0; + Handle->Count = Handle->Struct->FieldCounters[0]; + for (u32 i = 1; i < Handle->Struct->NumFields; ++i) { + Handle->Count += Handle->Struct->FieldCounters[i]; + Handle->Ratio += computeDifferenceRatio( + Handle->Struct->FieldCounters[i - 1], Handle->Struct->FieldCounters[i]); + } + Ctx->TotalCount += Handle->Count; + if (Handle->Ratio >= (u64)getFlags()->report_threshold || + (Verbosity() >= 1 && Handle->Count > 0)) + reportStructCounter(Handle); +} + +static void registerStructInfo(CacheFragInfo *CacheFrag) { + for (u32 i = 0; i < CacheFrag->NumStructs; ++i) { + StructInfo *Struct = &CacheFrag->Structs[i]; + StructHashMap::Handle H(&Ctx->StructMap, (uptr)Struct->FieldCounters); + if (H.created()) { + VPrintf(2, " Register %s: %u fields\n", Struct->StructName, + Struct->NumFields); + H->Struct = Struct; + ++Ctx->NumStructs; + } else { + VPrintf(2, " Duplicated %s: %u fields\n", Struct->StructName, + Struct->NumFields); + } + } +} + +static void unregisterStructInfo(CacheFragInfo *CacheFrag) { + // FIXME: if the library is unloaded before finalizeCacheFrag, we should + // collect the result for later report. + for (u32 i = 0; i < CacheFrag->NumStructs; ++i) { + StructInfo *Struct = &CacheFrag->Structs[i]; + StructHashMap::Handle H(&Ctx->StructMap, (uptr)Struct->FieldCounters, true); + if (H.exists()) { + VPrintf(2, " Unregister %s: %u fields\n", Struct->StructName, + Struct->NumFields); + // FIXME: we should move this call to finalizeCacheFrag once we can + // iterate over the hash map there. + computeStructRatio(H); + --Ctx->NumStructs; + } else { + VPrintf(2, " Duplicated %s: %u fields\n", Struct->StructName, + Struct->NumFields); + } + } + static bool Reported = false; + if (Ctx->NumStructs == 0 && !Reported) { + Reported = true; + reportStructSummary(); + } +} + +//===-- Init/exit functions -----------------------------------------------===// + +void processCacheFragCompilationUnitInit(void *Ptr) { + if (!getFlags()->build_mode) { + CacheFragInfo *CacheFrag = (CacheFragInfo *)Ptr; + VPrintf(2, "in esan::%s: %s with %u class(es)/struct(s)\n", __FUNCTION__, + CacheFrag->UnitName, CacheFrag->NumStructs); + registerStructInfo(CacheFrag); + } +} + +void processCacheFragCompilationUnitExit(void *Ptr) { + if (!getFlags()->build_mode) { + CacheFragInfo *CacheFrag = (CacheFragInfo *)Ptr; + VPrintf(2, "in esan::%s: %s with %u class(es)/struct(s)\n", __FUNCTION__, + CacheFrag->UnitName, CacheFrag->NumStructs); + unregisterStructInfo(CacheFrag); + } +} + +void initializeCacheFrag() { + if (!getFlags()->build_mode) { + VPrintf(2, "in esan::%s\n", __FUNCTION__); + // We use placement new to initialize Ctx before C++ static initializaion. + // We make CtxMem 8-byte aligned for atomic operations in AddrHashMap. + static u64 CtxMem[sizeof(Context) / sizeof(u64) + 1]; + Ctx = new (CtxMem) Context(); + Ctx->NumStructs = 0; + } +} + +int finalizeCacheFrag() { + VPrintf(2, "in esan::%s\n", __FUNCTION__); + return 0; +} + +void reportCacheFrag() { + VPrintf(2, "in esan::%s\n", __FUNCTION__); + // FIXME: Not yet implemented. We need to iterate over all of the + // compilation unit data. +} + +} // namespace __esan diff --git a/libsanitizer/esan/cache_frag.h b/libsanitizer/esan/cache_frag.h new file mode 100644 index 0000000..646d3f8 --- /dev/null +++ b/libsanitizer/esan/cache_frag.h @@ -0,0 +1,29 @@ +//===-- cache_frag.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Header for cache-fragmentation-specific code. +//===----------------------------------------------------------------------===// + +#ifndef CACHE_FRAG_H +#define CACHE_FRAG_H + +namespace __esan { + +void processCacheFragCompilationUnitInit(void *Ptr); +void processCacheFragCompilationUnitExit(void *Ptr); + +void initializeCacheFrag(); +int finalizeCacheFrag(); +void reportCacheFrag(); + +} // namespace __esan + +#endif // CACHE_FRAG_H diff --git a/libsanitizer/esan/esan.cpp b/libsanitizer/esan/esan.cpp new file mode 100644 index 0000000..e06af68 --- /dev/null +++ b/libsanitizer/esan/esan.cpp @@ -0,0 +1,313 @@ +//===-- esan.cpp ----------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Main file (entry points) for the Esan run-time. +//===----------------------------------------------------------------------===// + +#include "esan.h" +#include "esan_flags.h" +#include "esan_interface_internal.h" +#include "esan_shadow.h" +#include "cache_frag.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flag_parser.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "working_set.h" + +// See comment below. +extern "C" { +extern void __cxa_atexit(void (*function)(void)); +} + +// Delete this, after supporting Cache fragmentation tool. +//ToolType __esan_which_tool = ESAN_WorkingSet; +namespace __esan { + +bool EsanIsInitialized; +bool EsanDuringInit; +ShadowMapping Mapping; + +// Different tools use different scales within the same shadow mapping scheme. +// The scale used here must match that used by the compiler instrumentation. +// This array is indexed by the ToolType enum. +static const uptr ShadowScale[] = { + 0, // ESAN_None. + 2, // ESAN_CacheFrag: 4B:1B, so 4 to 1 == >>2. + 6, // ESAN_WorkingSet: 64B:1B, so 64 to 1 == >>6. +}; + +// We are combining multiple performance tuning tools under the umbrella of +// one EfficiencySanitizer super-tool. Most of our tools have very similar +// memory access instrumentation, shadow memory mapping, libc interception, +// etc., and there is typically more shared code than distinct code. +// +// We are not willing to dispatch on tool dynamically in our fastpath +// instrumentation: thus, which tool to use is a static option selected +// at compile time and passed to __esan_init(). +// +// We are willing to pay the overhead of tool dispatch in the slowpath to more +// easily share code. We expect to only come here rarely. +// If this becomes a performance hit, we can add separate interface +// routines for each subtool (e.g., __esan_cache_frag_aligned_load_4). +// But for libc interceptors, we'll have to do one of the following: +// A) Add multiple-include support to sanitizer_common_interceptors.inc, +// instantiate it separately for each tool, and call the selected +// tool's intercept setup code. +// B) Build separate static runtime libraries, one for each tool. +// C) Completely split the tools into separate sanitizers. + +void processRangeAccess(uptr PC, uptr Addr, int Size, bool IsWrite) { + // Could be a shadow fault while building the package on the OBS worker. + // So, just disable this by default. + if (getFlags()->process_range_access) { + VPrintf(3, "in esan::%s %p: %c %p %d\n", __FUNCTION__, PC, + IsWrite ? 'w' : 'r', Addr, Size); + if (__esan_which_tool == ESAN_CacheFrag) { + // TODO(bruening): add shadow mapping and update shadow bits here. + // We'll move this to cache_frag.cpp once we have something. + } else if (__esan_which_tool == ESAN_WorkingSet) { + processRangeAccessWorkingSet(PC, Addr, Size, IsWrite); + } + } +} + +void incrementFieldCounter(void *Addr) { + if (!getFlags()->build_mode) { + ++(*(unsigned long int *)Addr); + } +} + +bool processSignal(int SigNum, void (*Handler)(int), void (**Result)(int)) { + if (__esan_which_tool == ESAN_WorkingSet) + return processWorkingSetSignal(SigNum, Handler, Result); + return true; +} + +bool processSigaction(int SigNum, const void *Act, void *OldAct) { + if (__esan_which_tool == ESAN_WorkingSet) + return processWorkingSetSigaction(SigNum, Act, OldAct); + return true; +} + +bool processSigprocmask(int How, void *Set, void *OldSet) { + if (__esan_which_tool == ESAN_WorkingSet) + return processWorkingSetSigprocmask(How, Set, OldSet); + return true; +} + +#if SANITIZER_DEBUG +static bool verifyShadowScheme() { + // Sanity checks for our shadow mapping scheme. + uptr AppStart, AppEnd; + if (Verbosity() >= 3) { + for (int i = 0; getAppRegion(i, &AppStart, &AppEnd); ++i) { + VPrintf(3, "App #%d: [%zx-%zx) (%zuGB)\n", i, AppStart, AppEnd, + (AppEnd - AppStart) >> 30); + } + } + for (int Scale = 0; Scale < 8; ++Scale) { + Mapping.initialize(Scale); + if (Verbosity() >= 3) { + VPrintf(3, "\nChecking scale %d\n", Scale); + uptr ShadowStart, ShadowEnd; + for (int i = 0; getShadowRegion(i, &ShadowStart, &ShadowEnd); ++i) { + VPrintf(3, "Shadow #%d: [%zx-%zx) (%zuGB)\n", i, ShadowStart, + ShadowEnd, (ShadowEnd - ShadowStart) >> 30); + } + for (int i = 0; getShadowRegion(i, &ShadowStart, &ShadowEnd); ++i) { + VPrintf(3, "Shadow(Shadow) #%d: [%zx-%zx)\n", i, + appToShadow(ShadowStart), appToShadow(ShadowEnd - 1)+1); + } + } + for (int i = 0; getAppRegion(i, &AppStart, &AppEnd); ++i) { + DCHECK(isAppMem(AppStart)); + DCHECK(!isAppMem(AppStart - 1)); + DCHECK(isAppMem(AppEnd - 1)); + DCHECK(!isAppMem(AppEnd)); + DCHECK(!isShadowMem(AppStart)); + DCHECK(!isShadowMem(AppEnd - 1)); + DCHECK(isShadowMem(appToShadow(AppStart))); + DCHECK(isShadowMem(appToShadow(AppEnd - 1))); + // Double-shadow checks. + DCHECK(!isShadowMem(appToShadow(appToShadow(AppStart)))); + DCHECK(!isShadowMem(appToShadow(appToShadow(AppEnd - 1)))); + } + // Ensure no shadow regions overlap each other. + uptr ShadowAStart, ShadowBStart, ShadowAEnd, ShadowBEnd; + for (int i = 0; getShadowRegion(i, &ShadowAStart, &ShadowAEnd); ++i) { + for (int j = 0; getShadowRegion(j, &ShadowBStart, &ShadowBEnd); ++j) { + DCHECK(i == j || ShadowAStart >= ShadowBEnd || + ShadowAEnd <= ShadowBStart); + } + } + } + return true; +} +#endif + +uptr VmaSize; + +static void initializeShadow() { + verifyAddressSpace(); + + // This is based on the assumption that the intial stack is always allocated + // in the topmost segment of the user address space and the assumption + // holds true on all the platforms currently supported. + VmaSize = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); + + DCHECK(verifyShadowScheme()); + + Mapping.initialize(ShadowScale[__esan_which_tool]); + + VPrintf(1, "Shadow scale=%d offset=%p\n", Mapping.Scale, Mapping.Offset); + + uptr ShadowStart, ShadowEnd; +#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_MIPS64) + for (int i = 0; getShadowRegion(i, &ShadowStart, &ShadowEnd); ++i) { + VPrintf(1, "Shadow #%d: [%zx-%zx) (%zuGB)\n", i, ShadowStart, ShadowEnd, + (ShadowEnd - ShadowStart) >> 30); + + uptr Map; + if (__esan_which_tool == ESAN_WorkingSet) { + // We want to identify all shadow pages that are touched so we start + // out inaccessible. + Map = (uptr)MmapFixedNoAccess(ShadowStart, ShadowEnd - ShadowStart, + "shadow"); + } else { + Map = (uptr)MmapFixedNoReserve(ShadowStart, ShadowEnd - ShadowStart, + "shadow"); + } + if (Map != ShadowStart) { + Printf("FATAL: EfficiencySanitizer failed to map its shadow memory.\n"); + Die(); + } + + if (common_flags()->no_huge_pages_for_shadow) + NoHugePagesInRegion(ShadowStart, ShadowEnd - ShadowStart); + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(ShadowStart, ShadowEnd - ShadowStart); + + // TODO: Call MmapNoAccess() on in-between regions. + } +#else + // ARMVL + ShadowStart = 0x20000000; + ShadowEnd = 0x3fffffff; + uptr Map = + (uptr)MmapFixedNoReserve(ShadowStart, ShadowEnd - ShadowStart, "shadow"); + + if (Map != ShadowStart) { + Printf("FATAL : EfficiencySanitizer failed to map its shadow memory \n"); + Die(); + } + VPrintf(1, "Shadow [%zx-%zx) (%zuGB)\n", ShadowStart, ShadowEnd, + (ShadowEnd - ShadowStart) >> 20); + +#endif +} + +void initializeLibrary(ToolType Tool) { + // We assume there is only one thread during init, but we need to + // guard against double-init when we're (re-)called from an + // early interceptor. + if (EsanIsInitialized || EsanDuringInit) + return; + EsanDuringInit = true; + CHECK(Tool == __esan_which_tool); + SanitizerToolName = "EfficiencySanitizer"; + MaybeMountProcFS(); + CacheBinaryName(); + initializeFlags(); + + // Intercepting libc _exit or exit via COMMON_INTERCEPTOR_ON_EXIT only + // finalizes on an explicit exit call by the app. To handle a normal + // exit we register an atexit handler. + + // In case we run sanitized DSO but did not sanitize binary, + // we will get an segfault. Also can't set libesan to preload because + //__esan__which_tool comes from static DSO constructor, so if we + // do not have it, we will get a segfault. + if (Tool == ESAN_WorkingSet) + ::__cxa_atexit((void (*)())finalizeLibrary); + + VPrintf(1, "in esan::%s\n", __FUNCTION__); + if (__esan_which_tool <= ESAN_None || __esan_which_tool >= ESAN_Max) { + Printf("ERROR: unknown tool %d requested\n", __esan_which_tool); + Die(); + } + + if (__esan_which_tool == ESAN_WorkingSet) { + initializeShadow(); + initializeShadowWorkingSet(); + } + + initializeInterceptors(); + + if (__esan_which_tool == ESAN_CacheFrag) { + initializeCacheFrag(); + } else if (__esan_which_tool == ESAN_WorkingSet) { + initializeWorkingSet(); + } + + EsanIsInitialized = true; + EsanDuringInit = false; +} + +int finalizeLibrary() { + VPrintf(1, "in esan::%s\n", __FUNCTION__); + if (__esan_which_tool == ESAN_CacheFrag) { + return finalizeCacheFrag(); + } else if (__esan_which_tool == ESAN_WorkingSet) { + return finalizeWorkingSet(); + } + return 0; +} + +void reportResults() { + VPrintf(1, "in esan::%s\n", __FUNCTION__); + if (__esan_which_tool == ESAN_CacheFrag) { + return reportCacheFrag(); + } else if (__esan_which_tool == ESAN_WorkingSet) { + return reportWorkingSet(); + } +} + +void processCompilationUnitInit(void *Ptr) { + VPrintf(2, "in esan::%s\n", __FUNCTION__); + if (__esan_which_tool == ESAN_CacheFrag) { + DCHECK(Ptr != nullptr); + processCacheFragCompilationUnitInit(Ptr); + } else { + DCHECK(Ptr == nullptr); + } +} + +// This is called when the containing module is unloaded. +// For the main executable module, this is called after finalizeLibrary. +void processCompilationUnitExit(void *Ptr) { + VPrintf(2, "in esan::%s\n", __FUNCTION__); + if (__esan_which_tool == ESAN_CacheFrag) { + DCHECK(Ptr != nullptr); + processCacheFragCompilationUnitExit(Ptr); + } else { + DCHECK(Ptr == nullptr); + } +} + +unsigned int getSampleCount() { + VPrintf(1, "in esan::%s\n", __FUNCTION__); + if (__esan_which_tool == ESAN_WorkingSet) { + return getSampleCountWorkingSet(); + } + return 0; +} + +} // namespace __esan diff --git a/libsanitizer/esan/esan.h b/libsanitizer/esan/esan.h new file mode 100644 index 0000000..bb5ea9a --- /dev/null +++ b/libsanitizer/esan/esan.h @@ -0,0 +1,62 @@ +//===-- esan.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Main internal esan header file. +// +// Ground rules: +// - C++ run-time should not be used (static CTORs, RTTI, exceptions, static +// function-scope locals) +// - All functions/classes/etc reside in namespace __esan, except for those +// declared in esan_interface_internal.h. +// - Platform-specific files should be used instead of ifdefs (*). +// - No system headers included in header files (*). +// - Platform specific headers included only into platform-specific files (*). +// +// (*) Except when inlining is critical for performance. +//===----------------------------------------------------------------------===// + +#ifndef ESAN_H +#define ESAN_H + +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_common.h" +#include "esan_interface_internal.h" + +namespace __esan { + +extern bool EsanIsInitialized; +extern bool EsanDuringInit; +extern uptr VmaSize; + +void initializeLibrary(ToolType Tool); +int finalizeLibrary(); +void reportResults(); +unsigned int getSampleCount(); +// Esan creates the variable per tool per compilation unit at compile time +// and passes its pointer Ptr to the runtime library. +void processCompilationUnitInit(void *Ptr); +void processCompilationUnitExit(void *Ptr); +void processRangeAccess(uptr PC, uptr Addr, int Size, bool IsWrite); +void initializeInterceptors(); +void incrementFieldCounter(void *Addr); + +// Platform-dependent routines. +void verifyAddressSpace(); +bool fixMmapAddr(void **Addr, SIZE_T Size, int Flags); +uptr checkMmapResult(uptr Addr, SIZE_T Size); +// The return value indicates whether to call the real version or not. +bool processSignal(int SigNum, void (*Handler)(int), void (**Result)(int)); +bool processSigaction(int SigNum, const void *Act, void *OldAct); +bool processSigprocmask(int How, void *Set, void *OldSet); + +} // namespace __esan + +#endif // ESAN_H diff --git a/libsanitizer/esan/esan.syms.extra b/libsanitizer/esan/esan.syms.extra new file mode 100644 index 0000000..d6397d4 --- /dev/null +++ b/libsanitizer/esan/esan.syms.extra @@ -0,0 +1,4 @@ +__esan_init +__esan_exit +__esan_aligned* +__esan_unaligned* diff --git a/libsanitizer/esan/esan_circular_buffer.h b/libsanitizer/esan/esan_circular_buffer.h new file mode 100644 index 0000000..9ce102d --- /dev/null +++ b/libsanitizer/esan/esan_circular_buffer.h @@ -0,0 +1,96 @@ +//===-- esan_circular_buffer.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Circular buffer data structure. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_common.h" + +namespace __esan { + +// A circular buffer for POD data whose memory is allocated using mmap. +// There are two usage models: one is to use initialize/free (for global +// instances) and the other is to use placement new with the +// constructor and to call the destructor or free (they are equivalent). +template +class CircularBuffer { + public: + // To support global instances we cannot initialize any field in the + // default constructor. + explicit CircularBuffer() {} + CircularBuffer(uptr BufferCapacity) { + initialize(BufferCapacity); + WasConstructed = true; + } + ~CircularBuffer() { + if (WasConstructed) // Else caller will call free() explicitly. + free(); + } + void initialize(uptr BufferCapacity) { + Capacity = BufferCapacity; + // MmapOrDie rounds up to the page size for us. + Data = (T *)MmapOrDie(Capacity * sizeof(T), "CircularBuffer"); + StartIdx = 0; + Count = 0; + WasConstructed = false; + } + void free() { + UnmapOrDie(Data, Capacity * sizeof(T)); + } + T &operator[](uptr Idx) { + CHECK_LT(Idx, Count); + uptr ArrayIdx = (StartIdx + Idx) % Capacity; + return Data[ArrayIdx]; + } + const T &operator[](uptr Idx) const { + CHECK_LT(Idx, Count); + uptr ArrayIdx = (StartIdx + Idx) % Capacity; + return Data[ArrayIdx]; + } + void push_back(const T &Item) { + CHECK_GT(Capacity, 0); + uptr ArrayIdx = (StartIdx + Count) % Capacity; + Data[ArrayIdx] = Item; + if (Count < Capacity) + ++Count; + else + StartIdx = (StartIdx + 1) % Capacity; + } + T &back() { + CHECK_GT(Count, 0); + uptr ArrayIdx = (StartIdx + Count - 1) % Capacity; + return Data[ArrayIdx]; + } + void pop_back() { + CHECK_GT(Count, 0); + --Count; + } + uptr size() const { + return Count; + } + void clear() { + StartIdx = 0; + Count = 0; + } + bool empty() const { return size() == 0; } + + private: + CircularBuffer(const CircularBuffer&); + void operator=(const CircularBuffer&); + + bool WasConstructed; + T *Data; + uptr Capacity; + uptr StartIdx; + uptr Count; +}; + +} // namespace __esan diff --git a/libsanitizer/esan/esan_flags.cpp b/libsanitizer/esan/esan_flags.cpp new file mode 100644 index 0000000..803dcd3 --- /dev/null +++ b/libsanitizer/esan/esan_flags.cpp @@ -0,0 +1,68 @@ +//===-- esan_flags.cc -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Esan flag parsing logic. +//===----------------------------------------------------------------------===// + +#include "esan_flags.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flag_parser.h" +#include "sanitizer_common/sanitizer_flags.h" + +using namespace __sanitizer; + +namespace __esan { + +static const char EsanOptsEnv[] = "ESAN_OPTIONS"; + +Flags EsanFlagsDontUseDirectly; + +void Flags::setDefaults() { +#define ESAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "esan_flags.inc" +#undef ESAN_FLAG +} + +static void registerEsanFlags(FlagParser *Parser, Flags *F) { +#define ESAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(Parser, #Name, Description, &F->Name); +#include "esan_flags.inc" +#undef ESAN_FLAG +} + +void initializeFlags() { + SetCommonFlagsDefaults(); + Flags *F = getFlags(); + F->setDefaults(); + + FlagParser Parser; + registerEsanFlags(&Parser, F); + RegisterCommonFlags(&Parser); + + int mmaped = 0; + const char *options = + GetRuntimeOptions("ESAN_OPTIONS", "/ESAN_OPTIONS", &mmaped); + + Parser.ParseString(options); + + if (mmaped) + UnmapOrDie((void *)options, GetPageSizeCached()); + + InitializeCommonFlags(); + if (Verbosity()) + ReportUnrecognizedFlags(); + if (common_flags()->help) + Parser.PrintFlagDescriptions(); + + __sanitizer_set_report_path(common_flags()->log_path); +} + +} // namespace __esan diff --git a/libsanitizer/esan/esan_flags.h b/libsanitizer/esan/esan_flags.h new file mode 100644 index 0000000..c8f4ef5 --- /dev/null +++ b/libsanitizer/esan/esan_flags.h @@ -0,0 +1,41 @@ +//===-- esan_flags.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Esan runtime flags. +//===----------------------------------------------------------------------===// + +#ifndef ESAN_FLAGS_H +#define ESAN_FLAGS_H + +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_flag_parser.h" + +namespace __esan { + +class Flags { +public: +#define ESAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "esan_flags.inc" +#undef ESAN_FLAG + + void setDefaults(); +}; + +extern Flags EsanFlagsDontUseDirectly; +inline Flags *getFlags() { + return &EsanFlagsDontUseDirectly; +} + +void initializeFlags(); + +} // namespace __esan + +#endif // ESAN_FLAGS_H diff --git a/libsanitizer/esan/esan_flags.inc b/libsanitizer/esan/esan_flags.inc new file mode 100644 index 0000000..4129eec --- /dev/null +++ b/libsanitizer/esan/esan_flags.inc @@ -0,0 +1,59 @@ +//===-- esan_flags.inc ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Esan runtime flags. +// +//===----------------------------------------------------------------------===// + +#ifndef ESAN_FLAG +# error "Define ESAN_FLAG prior to including this file!" +#endif + +// ESAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +//===----------------------------------------------------------------------===// +// Cross-tool options +//===----------------------------------------------------------------------===// + +ESAN_FLAG(int, cache_line_size, 64, + "The number of bytes in a cache line. For the working-set tool, this " + "cannot be changed without also changing the compiler " + "instrumentation.") + +ESAN_FLAG(bool, process_range_access, false, + "Process range access.") + +//===----------------------------------------------------------------------===// +// Working set tool options +//===----------------------------------------------------------------------===// + +ESAN_FLAG(bool, record_snapshots, false, + "Working set tool: whether to sample snapshots during a run.") + +// Typical profiling uses a 10ms timer. Our snapshots take some work +// to scan memory so we reduce to 20ms. +// To disable samples, turn off record_snapshots. +ESAN_FLAG(int, sample_freq, 20, + "Working set tool: sampling frequency in milliseconds.") + +// This controls the difference in frequency between each successive series +// of snapshots. There are 8 in total, with number 0 using sample_freq. +// Number N samples number N-1 every (1 << snapshot_step) instance of N-1. +ESAN_FLAG(int, snapshot_step, 2, "Working set tool: the log of the sampling " + "performed for the next-higher-frequency snapshot series.") + +//===----------------------------------------------------------------------===// +// Cache Fragmentation tool options +//===----------------------------------------------------------------------===// +ESAN_FLAG(bool, build_mode, true, "Don't output any info under the build\n") +// The difference information of a struct is reported if the struct's difference +// score is greater than the report_threshold. +ESAN_FLAG(int, report_threshold, 1<<10, "Cache-frag tool: the struct difference" + " score threshold for reporting.") diff --git a/libsanitizer/esan/esan_hashtable.h b/libsanitizer/esan/esan_hashtable.h new file mode 100644 index 0000000..7bd8297 --- /dev/null +++ b/libsanitizer/esan/esan_hashtable.h @@ -0,0 +1,381 @@ +//===-- esan_hashtable.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Generic resizing hashtable. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_mutex.h" +#include + +namespace __esan { + +//===----------------------------------------------------------------------===// +// Default hash and comparison functions +//===----------------------------------------------------------------------===// + +template struct DefaultHash { + size_t operator()(const T &Key) const { + return (size_t)Key; + } +}; + +template struct DefaultEqual { + bool operator()(const T &Key1, const T &Key2) const { + return Key1 == Key2; + } +}; + +//===----------------------------------------------------------------------===// +// HashTable declaration +//===----------------------------------------------------------------------===// + +// A simple resizing and mutex-locked hashtable. +// +// If the default hash functor is used, KeyTy must have an operator size_t(). +// If the default comparison functor is used, KeyTy must have an operator ==. +// +// By default all operations are internally-synchronized with a mutex, with no +// synchronization for payloads once hashtable functions return. If +// ExternalLock is set to true, the caller should call the lock() and unlock() +// routines around all hashtable operations and subsequent manipulation of +// payloads. +template , + typename EqualFuncTy = DefaultEqual > +class HashTable { +public: + // InitialCapacity must be a power of 2. + // ResizeFactor must be between 1 and 99 and indicates the + // maximum percentage full that the table should ever be. + HashTable(u32 InitialCapacity = 2048, u32 ResizeFactor = 70); + ~HashTable(); + bool lookup(const KeyTy &Key, DataTy &Payload); // Const except for Mutex. + bool add(const KeyTy &Key, const DataTy &Payload); + bool remove(const KeyTy &Key); + u32 size(); // Const except for Mutex. + // If the table is internally-synchronized, this lock must not be held + // while a hashtable function is called as it will deadlock: the lock + // is not recursive. This is meant for use with externally-synchronized + // tables or with an iterator. + void lock(); + void unlock(); + +private: + struct HashEntry { + KeyTy Key; + DataTy Payload; + HashEntry *Next; + }; + +public: + struct HashPair { + HashPair(KeyTy Key, DataTy Data) : Key(Key), Data(Data) {} + KeyTy Key; + DataTy Data; + }; + + // This iterator does not perform any synchronization. + // It expects the caller to lock the table across the whole iteration. + // Calling HashTable functions while using the iterator is not supported. + // The iterator returns copies of the keys and data. + class iterator { + public: + iterator( + HashTable *Table); + iterator(const iterator &Src) = default; + iterator &operator=(const iterator &Src) = default; + HashPair operator*(); + iterator &operator++(); + iterator &operator++(int); + bool operator==(const iterator &Cmp) const; + bool operator!=(const iterator &Cmp) const; + + private: + iterator( + HashTable *Table, + int Idx); + friend HashTable; + HashTable *Table; + int Idx; + HashTable::HashEntry + *Entry; + }; + + // No erase or insert iterator supported + iterator begin(); + iterator end(); + +private: + void resize(); + + HashEntry **Table; + u32 Capacity; + u32 Entries; + const u32 ResizeFactor; + BlockingMutex Mutex; + const HashFuncTy HashFunc; + const EqualFuncTy EqualFunc; +}; + +//===----------------------------------------------------------------------===// +// Hashtable implementation +//===----------------------------------------------------------------------===// + +template +HashTable::HashTable( + u32 InitialCapacity, u32 ResizeFactor) + : Capacity(InitialCapacity), Entries(0), ResizeFactor(ResizeFactor), + HashFunc(HashFuncTy()), EqualFunc(EqualFuncTy()) { + CHECK(IsPowerOfTwo(Capacity)); + CHECK(ResizeFactor >= 1 && ResizeFactor <= 99); + Table = (HashEntry **)InternalAlloc(Capacity * sizeof(HashEntry *)); + internal_memset(Table, 0, Capacity * sizeof(HashEntry *)); +} + +template +HashTable::~HashTable() { + for (u32 i = 0; i < Capacity; ++i) { + HashEntry *Entry = Table[i]; + while (Entry != nullptr) { + HashEntry *Next = Entry->Next; + Entry->Payload.~DataTy(); + InternalFree(Entry); + Entry = Next; + } + } + InternalFree(Table); +} + +template +u32 HashTable::size() { + u32 Res; + if (!ExternalLock) + Mutex.Lock(); + Res = Entries; + if (!ExternalLock) + Mutex.Unlock(); + return Res; +} + +template +bool HashTable::lookup( + const KeyTy &Key, DataTy &Payload) { + if (!ExternalLock) + Mutex.Lock(); + bool Found = false; + size_t Hash = HashFunc(Key) % Capacity; + HashEntry *Entry = Table[Hash]; + for (; Entry != nullptr; Entry = Entry->Next) { + if (EqualFunc(Entry->Key, Key)) { + Payload = Entry->Payload; + Found = true; + break; + } + } + if (!ExternalLock) + Mutex.Unlock(); + return Found; +} + +template +void HashTable::resize() { + if (!ExternalLock) + Mutex.CheckLocked(); + size_t OldCapacity = Capacity; + HashEntry **OldTable = Table; + Capacity *= 2; + Table = (HashEntry **)InternalAlloc(Capacity * sizeof(HashEntry *)); + internal_memset(Table, 0, Capacity * sizeof(HashEntry *)); + // Re-hash + for (u32 i = 0; i < OldCapacity; ++i) { + HashEntry *OldEntry = OldTable[i]; + while (OldEntry != nullptr) { + HashEntry *Next = OldEntry->Next; + size_t Hash = HashFunc(OldEntry->Key) % Capacity; + OldEntry->Next = Table[Hash]; + Table[Hash] = OldEntry; + OldEntry = Next; + } + } +} + +template +bool HashTable::add( + const KeyTy &Key, const DataTy &Payload) { + if (!ExternalLock) + Mutex.Lock(); + bool Exists = false; + size_t Hash = HashFunc(Key) % Capacity; + HashEntry *Entry = Table[Hash]; + for (; Entry != nullptr; Entry = Entry->Next) { + if (EqualFunc(Entry->Key, Key)) { + Exists = true; + break; + } + } + if (!Exists) { + Entries++; + if (Entries * 100 >= Capacity * ResizeFactor) { + resize(); + Hash = HashFunc(Key) % Capacity; + } + HashEntry *Add = (HashEntry *)InternalAlloc(sizeof(*Add)); + Add->Key = Key; + Add->Payload = Payload; + Add->Next = Table[Hash]; + Table[Hash] = Add; + } + if (!ExternalLock) + Mutex.Unlock(); + return !Exists; +} + +template +bool HashTable::remove( + const KeyTy &Key) { + if (!ExternalLock) + Mutex.Lock(); + bool Found = false; + size_t Hash = HashFunc(Key) % Capacity; + HashEntry *Entry = Table[Hash]; + HashEntry *Prev = nullptr; + for (; Entry != nullptr; Prev = Entry, Entry = Entry->Next) { + if (EqualFunc(Entry->Key, Key)) { + Found = true; + Entries--; + if (Prev == nullptr) + Table[Hash] = Entry->Next; + else + Prev->Next = Entry->Next; + Entry->Payload.~DataTy(); + InternalFree(Entry); + break; + } + } + if (!ExternalLock) + Mutex.Unlock(); + return Found; +} + +template +void HashTable::lock() { + Mutex.Lock(); +} + +template +void HashTable::unlock() { + Mutex.Unlock(); +} + +//===----------------------------------------------------------------------===// +// Iterator implementation +//===----------------------------------------------------------------------===// + +template +HashTable::iterator:: + iterator( + HashTable *Table) + : Table(Table), Idx(-1), Entry(nullptr) { + operator++(); +} + +template +HashTable::iterator:: + iterator( + HashTable *Table, + int Idx) + : Table(Table), Idx(Idx), Entry(nullptr) { + CHECK(Idx >= (int)Table->Capacity); // Only used to create end(). +} + +template +typename HashTable::HashPair + HashTable::iterator:: + operator*() { + CHECK(Idx >= 0 && Idx < (int)Table->Capacity); + CHECK(Entry != nullptr); + return HashPair(Entry->Key, Entry->Payload); +} + +template +typename HashTable::iterator & + HashTable::iterator:: + operator++() { + if (Entry != nullptr) + Entry = Entry->Next; + while (Entry == nullptr) { + ++Idx; + if (Idx >= (int)Table->Capacity) + break; // At end(). + Entry = Table->Table[Idx]; + } + return *this; +} + +template +typename HashTable::iterator & + HashTable::iterator:: + operator++(int) { + iterator Temp(*this); + operator++(); + return Temp; +} + +template +bool HashTable::iterator:: +operator==(const iterator &Cmp) const { + return Cmp.Table == Table && Cmp.Idx == Idx && Cmp.Entry == Entry; +} + +template +bool HashTable::iterator:: +operator!=(const iterator &Cmp) const { + return Cmp.Table != Table || Cmp.Idx != Idx || Cmp.Entry != Entry; +} + +template +typename HashTable::iterator +HashTable::begin() { + return iterator(this); +} + +template +typename HashTable::iterator +HashTable::end() { + return iterator(this, Capacity); +} + +} // namespace __esan diff --git a/libsanitizer/esan/esan_interceptors.cpp b/libsanitizer/esan/esan_interceptors.cpp new file mode 100644 index 0000000..44391b9 --- /dev/null +++ b/libsanitizer/esan/esan_interceptors.cpp @@ -0,0 +1,568 @@ +//===-- esan_interceptors.cpp ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Interception routines for the esan run-time. +//===----------------------------------------------------------------------===// + +#include "esan.h" +#include "esan_shadow.h" +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_linux.h" +#include "sanitizer_common/sanitizer_stacktrace.h" + +using namespace __esan; // NOLINT + +#define CUR_PC() (StackTrace::GetCurrentPc()) + +//===----------------------------------------------------------------------===// +// Interception via sanitizer common interceptors +//===----------------------------------------------------------------------===// + +// Get the per-platform defines for what is possible to intercept +#include "sanitizer_common/sanitizer_platform_interceptors.h" + +// TODO(bruening): tsan disables several interceptors (getpwent, etc.) claiming +// that interception is a perf hit: should we do the same? + +// We don't need to intercept this. +#undef SANITIZER_INTERCEPT_TLS_GET_ADDR +#undef SANITIZER_INTERCEPT_FORTIFY_SOURCE + +// TODO(bruening): the common realpath interceptor assumes malloc is +// intercepted! We should try to parametrize that, though we'll +// intercept malloc soon ourselves and can then remove this undef. +#undef SANITIZER_INTERCEPT_REALPATH + +// We provide our own version: +#undef SANITIZER_INTERCEPT_SIGPROCMASK + +// Could cause a segfault see: https://github.com/google/sanitizers/issues/321. +#undef SANITIZER_INTERCEPT_CLOCK_GETTIME + +#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!EsanIsInitialized) + +#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name) +#define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \ + INTERCEPT_FUNCTION_VER(name, ver) + +// We must initialize during early interceptors, to support tcmalloc. +// This means that for some apps we fully initialize prior to +// __esan_init() being called. +// We currently do not use ctx. +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + do { \ + if (UNLIKELY(COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)) { \ + if (!UNLIKELY(EsanDuringInit)) \ + initializeLibrary(__esan_which_tool); \ + return REAL(func)(__VA_ARGS__); \ + } \ + ctx = nullptr; \ + (void)ctx; \ + } while (false) + +#define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, func, ...) \ + COMMON_INTERCEPTOR_ENTER(ctx, func, __VA_ARGS__) + +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ + processRangeAccess(CUR_PC(), (uptr)ptr, size, true) + +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ + processRangeAccess(CUR_PC(), (uptr)ptr, size, false) + +// This is only called if the app explicitly calls exit(), not on +// a normal exit. +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) finalizeLibrary() + +#define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) \ + do { \ + (void)(ctx); \ + (void)(file); \ + (void)(path); \ + } while (false) +#define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) \ + do { \ + (void)(ctx); \ + (void)(file); \ + } while (false) +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \ + do { \ + (void)(filename); \ + (void)(handle); \ + } while (false) +#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) \ + do { \ + (void)(ctx); \ + (void)(u); \ + } while (false) +#define COMMON_INTERCEPTOR_RELEASE(ctx, u) \ + do { \ + (void)(ctx); \ + (void)(u); \ + } while (false) +#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ + do { \ + (void)(ctx); \ + (void)(path); \ + } while (false) +#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ + do { \ + (void)(ctx); \ + (void)(fd); \ + } while (false) +#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ + do { \ + (void)(ctx); \ + (void)(fd); \ + } while (false) +#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \ + do { \ + (void)(ctx); \ + (void)(fd); \ + } while (false) +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + do { \ + (void)(ctx); \ + (void)(fd); \ + (void)(newfd); \ + } while (false) +#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ + do { \ + (void)(ctx); \ + (void)(name); \ + } while (false) +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + do { \ + (void)(ctx); \ + (void)(thread); \ + (void)(name); \ + } while (false) +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) +#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) \ + do { \ + (void)(ctx); \ + (void)(m); \ + } while (false) +#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \ + do { \ + (void)(ctx); \ + (void)(m); \ + } while (false) +#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \ + do { \ + (void)(ctx); \ + (void)(m); \ + } while (false) +#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \ + do { \ + (void)(ctx); \ + (void)(msg); \ + } while (false) +#define COMMON_INTERCEPTOR_USER_CALLBACK_START() \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_USER_CALLBACK_END() \ + do { \ + } while (false) + +#include "sanitizer_common/sanitizer_common_interceptors.inc" + +//===----------------------------------------------------------------------===// +// Syscall interception +//===----------------------------------------------------------------------===// + +// We want the caller's PC b/c unlike the other function interceptors these +// are separate pre and post functions called around the app's syscall(). + +#define COMMON_SYSCALL_PRE_READ_RANGE(ptr, size) \ + processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, false) + +#define COMMON_SYSCALL_PRE_WRITE_RANGE(ptr, size) \ + do { \ + (void)(ptr); \ + (void)(size); \ + } while (false) + +#define COMMON_SYSCALL_POST_READ_RANGE(ptr, size) \ + do { \ + (void)(ptr); \ + (void)(size); \ + } while (false) + +// The actual amount written is in post, not pre. +#define COMMON_SYSCALL_POST_WRITE_RANGE(ptr, size) \ + processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, true) + +#define COMMON_SYSCALL_ACQUIRE(addr) \ + do { \ + (void)(addr); \ + } while (false) +#define COMMON_SYSCALL_RELEASE(addr) \ + do { \ + (void)(addr); \ + } while (false) +#define COMMON_SYSCALL_FD_CLOSE(fd) \ + do { \ + (void)(fd); \ + } while (false) +#define COMMON_SYSCALL_FD_ACQUIRE(fd) \ + do { \ + (void)(fd); \ + } while (false) +#define COMMON_SYSCALL_FD_RELEASE(fd) \ + do { \ + (void)(fd); \ + } while (false) +#define COMMON_SYSCALL_PRE_FORK() \ + do { \ + } while (false) +#define COMMON_SYSCALL_POST_FORK(res) \ + do { \ + (void)(res); \ + } while (false) + +#include "sanitizer_common/sanitizer_common_syscalls.inc" + +//===----------------------------------------------------------------------===// +// Custom interceptors +//===----------------------------------------------------------------------===// + +// TODO(bruening): move more of these to the common interception pool as they +// are shared with tsan and asan. +// While our other files match LLVM style, here we match sanitizer style as we +// expect to move these to the common pool. + +INTERCEPTOR(char *, strcpy, char *dst, const char *src) { // NOLINT + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcpy, dst, src); + uptr srclen = internal_strlen(src); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, srclen + 1); + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, srclen + 1); + return REAL(strcpy)(dst, src); // NOLINT +} + +INTERCEPTOR(char *, strncpy, char *dst, char *src, uptr n) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strncpy, dst, src, n); + uptr srclen = internal_strnlen(src, n); + uptr copied_size = srclen + 1 > n ? n : srclen + 1; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, copied_size); + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, copied_size); + return REAL(strncpy)(dst, src, n); +} + +INTERCEPTOR(int, open, const char *name, int flags, int mode) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, open, name, flags, mode); + COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0); + return REAL(open)(name, flags, mode); +} + +#if SANITIZER_LINUX +INTERCEPTOR(int, open64, const char *name, int flags, int mode) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, open64, name, flags, mode); + COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0); + return REAL(open64)(name, flags, mode); +} +#define ESAN_MAYBE_INTERCEPT_OPEN64 INTERCEPT_FUNCTION(open64) +#else +#define ESAN_MAYBE_INTERCEPT_OPEN64 +#endif + +INTERCEPTOR(int, creat, const char *name, int mode) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, creat, name, mode); + COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0); + return REAL(creat)(name, mode); +} + +#if SANITIZER_LINUX +INTERCEPTOR(int, creat64, const char *name, int mode) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, creat64, name, mode); + COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0); + return REAL(creat64)(name, mode); +} +#define ESAN_MAYBE_INTERCEPT_CREAT64 INTERCEPT_FUNCTION(creat64) +#else +#define ESAN_MAYBE_INTERCEPT_CREAT64 +#endif + +INTERCEPTOR(int, unlink, char *path) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, unlink, path); + COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); + return REAL(unlink)(path); +} + +INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fread, ptr, size, nmemb, f); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size * nmemb); + return REAL(fread)(ptr, size, nmemb, f); +} + +INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fwrite, p, size, nmemb, f); + COMMON_INTERCEPTOR_READ_RANGE(ctx, p, size * nmemb); + return REAL(fwrite)(p, size, nmemb, f); +} + +INTERCEPTOR(int, puts, const char *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, puts, s); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s, internal_strlen(s)); + return REAL(puts)(s); +} + +INTERCEPTOR(int, rmdir, char *path) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, rmdir, path); + COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); + return REAL(rmdir)(path); +} + +//===----------------------------------------------------------------------===// +// Shadow-related interceptors +//===----------------------------------------------------------------------===// + +// These are candidates for sharing with all sanitizers if shadow memory +// support is also standardized. + +INTERCEPTOR(void *, mmap, void *addr, SIZE_T sz, int prot, int flags, + int fd, OFF_T off) { + if (UNLIKELY(REAL(mmap) == nullptr)) { + // With esan init during interceptor init and a static libc preventing + // our early-calloc from triggering, we can end up here before our + // REAL pointer is set up. + return (void *)internal_mmap(addr, sz, prot, flags, fd, off); + } + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, mmap, addr, sz, prot, flags, fd, off); + if (!fixMmapAddr(&addr, sz, flags)) + return (void *)-1; + void *result = REAL(mmap)(addr, sz, prot, flags, fd, off); + return (void *)checkMmapResult((uptr)result, sz); +} + +#if SANITIZER_LINUX +INTERCEPTOR(void *, mmap64, void *addr, SIZE_T sz, int prot, int flags, + int fd, OFF64_T off) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, mmap64, addr, sz, prot, flags, fd, off); + if (!fixMmapAddr(&addr, sz, flags)) + return (void *)-1; + void *result = REAL(mmap64)(addr, sz, prot, flags, fd, off); + return (void *)checkMmapResult((uptr)result, sz); +} +#define ESAN_MAYBE_INTERCEPT_MMAP64 INTERCEPT_FUNCTION(mmap64) +#else +#define ESAN_MAYBE_INTERCEPT_MMAP64 +#endif + +//===----------------------------------------------------------------------===// +// Signal-related interceptors +//===----------------------------------------------------------------------===// + +#if SANITIZER_LINUX +typedef void (*signal_handler_t)(int); +INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, signal, signum, handler); + signal_handler_t result; + if (!processSignal(signum, handler, &result)) + return result; + else + return REAL(signal)(signum, handler); +} +#define ESAN_MAYBE_INTERCEPT_SIGNAL INTERCEPT_FUNCTION(signal) +#else +#error Platform not supported +#define ESAN_MAYBE_INTERCEPT_SIGNAL +#endif + +#if SANITIZER_LINUX +DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact) +INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigaction, signum, act, oldact); + if (!processSigaction(signum, act, oldact)) + return 0; + else + return REAL(sigaction)(signum, act, oldact); +} + +// This is required to properly use internal_sigaction. +namespace __sanitizer { +#if (defined(__x86_64__) || SANITIZER_MIPS64) && !SANITIZER_GO +int real_sigaction(int signum, const void *act, void *oldact) { + if (REAL(sigaction) == nullptr) { + // With an instrumented allocator, this is called during interceptor init + // and we need a raw syscall solution. + return internal_sigaction_syscall(signum, act, oldact); + } + return REAL(sigaction)(signum, (const struct sigaction *)act, + (struct sigaction *)oldact); +} +#endif +} // namespace __sanitizer + +#define ESAN_MAYBE_INTERCEPT_SIGACTION INTERCEPT_FUNCTION(sigaction) +#else +#error Platform not supported +#define ESAN_MAYBE_INTERCEPT_SIGACTION +#endif + +#if SANITIZER_LINUX +INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset); + int res = 0; + if (processSigprocmask(how, set, oldset)) + res = REAL(sigprocmask)(how, set, oldset); + if (!res && oldset) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset)); + return res; +} +#define ESAN_MAYBE_INTERCEPT_SIGPROCMASK INTERCEPT_FUNCTION(sigprocmask) +#else +#define ESAN_MAYBE_INTERCEPT_SIGPROCMASK +#endif + +//This interceptor causes to crash coreclr +#if !SANITIZER_WINDOWS && !SANITIZER_LINUX +INTERCEPTOR(int, pthread_sigmask, int how, __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_sigmask, how, set, oldset); + int res = 0; + if (processSigprocmask(how, set, oldset)) + res = REAL(sigprocmask)(how, set, oldset); + if (!res && oldset) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset)); + return res; +} +#define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK INTERCEPT_FUNCTION(pthread_sigmask) +#else +#define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK +#endif + +//===----------------------------------------------------------------------===// +// Malloc interceptors +//===----------------------------------------------------------------------===// + +static const uptr early_alloc_buf_size = 4096; +static uptr allocated_bytes; +static char early_alloc_buf[early_alloc_buf_size]; + +static bool isInEarlyAllocBuf(const void *ptr) { + return ((uptr)ptr >= (uptr)early_alloc_buf && + ((uptr)ptr - (uptr)early_alloc_buf) < sizeof(early_alloc_buf)); +} + +static void *handleEarlyAlloc(uptr size) { + // If esan is initialized during an interceptor (which happens with some + // tcmalloc implementations that call pthread_mutex_lock), the call from + // dlsym to calloc will deadlock. + // dlsym may also call malloc before REAL(malloc) is retrieved from dlsym. + // We work around it by using a static buffer for the early malloc/calloc + // requests. + // This solution will also allow us to deliberately intercept malloc & family + // in the future (to perform tool actions on each allocation, without + // replacing the allocator), as it also solves the problem of intercepting + // calloc when it will itself be called before its REAL pointer is + // initialized. + // We do not handle multiple threads here. This only happens at process init + // time, and while it's possible for a shared library to create early threads + // that race here, we consider that to be a corner case extreme enough that + // it's not worth the effort to handle. + void *mem = (void *)&early_alloc_buf[allocated_bytes]; + allocated_bytes += size; + CHECK_LT(allocated_bytes, early_alloc_buf_size); + return mem; +} + +INTERCEPTOR(void*, calloc, uptr size, uptr n) { + if (EsanDuringInit && REAL(calloc) == nullptr) + return handleEarlyAlloc(size * n); + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, calloc, size, n); + void *res = REAL(calloc)(size, n); + // The memory is zeroed and thus is all written. + COMMON_INTERCEPTOR_WRITE_RANGE(nullptr, (uptr)res, size * n); + return res; +} + +INTERCEPTOR(void*, malloc, uptr size) { + if (EsanDuringInit && REAL(malloc) == nullptr) + return handleEarlyAlloc(size); + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, malloc, size); + return REAL(malloc)(size); +} + +INTERCEPTOR(void, free, void *p) { + void *ctx; + // There are only a few early allocation requests, so we simply skip the free. + if (isInEarlyAllocBuf(p)) + return; + COMMON_INTERCEPTOR_ENTER(ctx, free, p); + REAL(free)(p); +} + +namespace __esan { + +void initializeInterceptors() { + InitializeCommonInterceptors(); + + INTERCEPT_FUNCTION(strcpy); // NOLINT + INTERCEPT_FUNCTION(strncpy); + + INTERCEPT_FUNCTION(open); + ESAN_MAYBE_INTERCEPT_OPEN64; + INTERCEPT_FUNCTION(creat); + ESAN_MAYBE_INTERCEPT_CREAT64; + INTERCEPT_FUNCTION(unlink); + INTERCEPT_FUNCTION(fread); + INTERCEPT_FUNCTION(fwrite); + INTERCEPT_FUNCTION(puts); + INTERCEPT_FUNCTION(rmdir); + + INTERCEPT_FUNCTION(mmap); + ESAN_MAYBE_INTERCEPT_MMAP64; + + ESAN_MAYBE_INTERCEPT_SIGNAL; + ESAN_MAYBE_INTERCEPT_SIGACTION; + ESAN_MAYBE_INTERCEPT_SIGPROCMASK; + ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK; + + INTERCEPT_FUNCTION(calloc); + INTERCEPT_FUNCTION(malloc); + INTERCEPT_FUNCTION(free); + + // TODO(bruening): intercept routines that other sanitizers intercept that + // are not in the common pool or here yet, ideally by adding to the common + // pool. Examples include wcslen and bcopy. + + // TODO(bruening): there are many more libc routines that read or write data + // structures that no sanitizer is intercepting: sigaction, strtol, etc. +} + +} // namespace __esan diff --git a/libsanitizer/esan/esan_interface.cpp b/libsanitizer/esan/esan_interface.cpp new file mode 100644 index 0000000..0ca0da8 --- /dev/null +++ b/libsanitizer/esan/esan_interface.cpp @@ -0,0 +1,122 @@ +//===-- esan_interface.cpp ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +//===----------------------------------------------------------------------===// + +#include "esan_interface_internal.h" +#include "esan.h" +#include "sanitizer_common/sanitizer_internal_defs.h" + +using namespace __esan; // NOLINT + +void __esan_init(ToolType Tool, void *Ptr) { + if (Tool != __esan_which_tool) { + Printf("ERROR: tool mismatch: %d vs %d\n", Tool, __esan_which_tool); + Die(); + } + initializeLibrary(Tool); + processCompilationUnitInit(Ptr); +} + +void __esan_exit(void *Ptr) { + processCompilationUnitExit(Ptr); +} + +void __esan_aligned_load1(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 1, false); +} + +void __esan_aligned_load2(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, false); +} + +void __esan_aligned_load4(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, false); +} + +void __esan_aligned_load8(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, false); +} + +void __esan_aligned_load16(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, false); +} + +void __esan_aligned_store1(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 1, true); +} + +void __esan_aligned_store2(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, true); +} + +void __esan_aligned_store4(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, true); +} + +void __esan_aligned_store8(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, true); +} + +void __esan_aligned_store16(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, true); +} + +void __esan_unaligned_load2(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, false); +} + +void __esan_unaligned_load4(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, false); +} + +void __esan_unaligned_load8(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, false); +} + +void __esan_unaligned_load16(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, false); +} + +void __esan_unaligned_store2(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, true); +} + +void __esan_unaligned_store4(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, true); +} + +void __esan_unaligned_store8(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, true); +} + +void __esan_unaligned_store16(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, true); +} + +void __esan_unaligned_loadN(void *Addr, uptr Size) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, Size, false); +} + +void __esan_unaligned_storeN(void *Addr, uptr Size) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, Size, true); +} + +void __esan_increment(void *Addr) { incrementFieldCounter(Addr); } + +// Public interface: +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE void __esan_report() { reportResults(); } + +SANITIZER_INTERFACE_ATTRIBUTE unsigned int __esan_get_sample_count() { + return getSampleCount(); +} +} // extern "C" diff --git a/libsanitizer/esan/esan_interface_internal.h b/libsanitizer/esan/esan_interface_internal.h new file mode 100644 index 0000000..82311a6 --- /dev/null +++ b/libsanitizer/esan/esan_interface_internal.h @@ -0,0 +1,84 @@ +//===-- esan_interface_internal.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Calls to the functions declared in this header will be inserted by +// the instrumentation module. +//===----------------------------------------------------------------------===// + +#ifndef ESAN_INTERFACE_INTERNAL_H +#define ESAN_INTERFACE_INTERNAL_H + +#include + +// This header should NOT include any other headers. +// All functions in this header are extern "C" and start with __esan_. + +using __sanitizer::uptr; +using __sanitizer::u32; + +extern "C" { + +// This should be kept consistent with LLVM's EfficiencySanitizerOptions. +// The value is passed as a 32-bit integer by the compiler. +typedef enum Type : u32 { + ESAN_None = 0, + ESAN_CacheFrag, + ESAN_WorkingSet, + ESAN_Max, +} ToolType; + +// To handle interceptors that invoke instrumented code prior to +// __esan_init() being called, the instrumentation module creates this +// global variable specifying the tool. +extern ToolType __esan_which_tool __attribute__((weak)); + +// This function should be called at the very beginning of the process, +// before any instrumented code is executed and before any call to malloc. +SANITIZER_INTERFACE_ATTRIBUTE void __esan_init(ToolType Tool, void *Ptr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_exit(void *Ptr); + +// The instrumentation module will insert a call to one of these routines prior +// to each load and store instruction for which we do not have "fastpath" +// inlined instrumentation. These calls constitute the "slowpath" for our +// tools. We have separate routines for each type of memory access to enable +// targeted optimization. +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load1(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load2(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load4(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load8(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load16(void *Addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store1(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store2(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store4(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store8(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store16(void *Addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load2(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load4(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load8(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load16(void *Addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store2(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store4(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store8(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store16(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_increment(void *Addr); + +// These cover unusually-sized accesses. +SANITIZER_INTERFACE_ATTRIBUTE +void __esan_unaligned_loadN(void *Addr, uptr Size); +SANITIZER_INTERFACE_ATTRIBUTE +void __esan_unaligned_storeN(void *Addr, uptr Size); + +} // extern "C" + +#endif // ESAN_INTERFACE_INTERNAL_H diff --git a/libsanitizer/esan/esan_linux.cpp b/libsanitizer/esan/esan_linux.cpp new file mode 100644 index 0000000..014205c --- /dev/null +++ b/libsanitizer/esan/esan_linux.cpp @@ -0,0 +1,83 @@ +//===-- esan.cpp ----------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Linux-specific code for the Esan run-time. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_FREEBSD || SANITIZER_LINUX + +#include "esan.h" +#include "esan_shadow.h" +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_common.h" +#include +#include + +namespace __esan { + +void verifyAddressSpace() { +#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_MIPS64) + // The kernel determines its mmap base from the stack size limit. + // Our Linux 64-bit shadow mapping assumes the stack limit is less than a + // terabyte, which keeps the mmap region above 0x7e00'. + uptr StackLimit = GetStackSizeLimitInBytes(); + if (StackSizeIsUnlimited() || StackLimit > MaxStackSize) { + VReport(1, "The stack size limit is beyond the maximum supported.\n" + "Re-execing with a stack size below 1TB.\n"); + SetStackSizeLimitInBytes(MaxStackSize); + ReExec(); + } +#endif +} + +static bool liesWithinSingleAppRegion(uptr Start, SIZE_T Size) { + uptr AppStart, AppEnd; + for (int i = 0; getAppRegion(i, &AppStart, &AppEnd); ++i) { + if (Start >= AppStart && Start + Size - 1 <= AppEnd) { + return true; + } + } + return false; +} + +bool fixMmapAddr(void **Addr, SIZE_T Size, int Flags) { + if (*Addr) { + if (!liesWithinSingleAppRegion((uptr)*Addr, Size)) { + VPrintf(1, "mmap conflict: [%p-%p) is not in an app region\n", + *Addr, (uptr)*Addr + Size); + if (Flags & MAP_FIXED) { + errno = EINVAL; + return false; + } else { + *Addr = 0; + } + } + } + return true; +} + +uptr checkMmapResult(uptr Addr, SIZE_T Size) { + if ((void *)Addr == MAP_FAILED) + return Addr; + if (!liesWithinSingleAppRegion(Addr, Size)) { + // FIXME: attempt to dynamically add this as an app region if it + // fits our shadow criteria. + // We could also try to remap somewhere else. + Printf("ERROR: unsupported mapping at [%p-%p)\n", Addr, Addr+Size); + Die(); + } + return Addr; +} + +} // namespace __esan + +#endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/libsanitizer/esan/esan_shadow.h b/libsanitizer/esan/esan_shadow.h new file mode 100644 index 0000000..c4b0bde --- /dev/null +++ b/libsanitizer/esan/esan_shadow.h @@ -0,0 +1,318 @@ +//===-- esan_shadow.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Shadow memory mappings for the esan run-time. +//===----------------------------------------------------------------------===// + +#ifndef ESAN_SHADOW_H +#define ESAN_SHADOW_H + +#include "esan.h" +#include + +namespace __esan { + +struct ApplicationRegion { + uptr Start; + uptr End; + bool ShadowMergedWithPrev; +}; + +#if SANITIZER_LINUX && defined(__x86_64__) +// Linux x86_64 +// +// Application memory falls into these 5 regions (ignoring the corner case +// of PIE with a non-zero PT_LOAD base): +// +// [0x00000000'00000000, 0x00000100'00000000) non-PIE + heap +// [0x00005500'00000000, 0x00005700'00000000) PIE +// [0x00007e00'00000000, 0x00007fff'ff600000) libraries + stack, part 1 +// [0x00007fff'ff601000, 0x00008000'00000000) libraries + stack, part 2 +// [0xffffffff'ff600000, 0xffffffff'ff601000) vsyscall +// +// Although we can ignore the vsyscall for the most part as there are few data +// references there (other sanitizers ignore it), we enforce a gap inside the +// library region to distinguish the vsyscall's shadow, considering this gap to +// be an invalid app region. +// We disallow application memory outside of those 5 regions. +// Our regions assume that the stack rlimit is less than a terabyte (otherwise +// the Linux kernel's default mmap region drops below 0x7e00'), which we enforce +// at init time (we can support larger and unlimited sizes for shadow +// scaledowns, but it is difficult for 1:1 mappings). +// +// Our shadow memory is scaled from a 1:1 mapping and supports a scale +// specified at library initialization time that can be any power-of-2 +// scaledown (1x, 2x, 4x, 8x, 16x, etc.). +// +// We model our shadow memory after Umbra, a library used by the Dr. Memory +// tool: https://github.com/DynamoRIO/drmemory/blob/master/umbra/umbra_x64.c. +// We use Umbra's scheme as it was designed to support different +// offsets, it supports two different shadow mappings (which we may want to +// use for future tools), and it ensures that the shadow of a shadow will +// not overlap either shadow memory or application memory. +// +// This formula translates from application memory to shadow memory: +// +// shadow(app) = ((app & 0x00000fff'ffffffff) + offset) >> scale +// +// Where the offset for 1:1 is 0x00001300'00000000. For other scales, the +// offset is shifted left by the scale, except for scales of 1 and 2 where +// it must be tweaked in order to pass the double-shadow test +// (see the "shadow(shadow)" comments below): +// scale == 0: 0x00001300'000000000 +// scale == 1: 0x00002200'000000000 +// scale == 2: 0x00004400'000000000 +// scale >= 3: (0x00001300'000000000 << scale) +// +// Do not pass in the open-ended end value to the formula as it will fail. +// +// The resulting shadow memory regions for a 0 scaling are: +// +// [0x00001300'00000000, 0x00001400'00000000) +// [0x00001800'00000000, 0x00001a00'00000000) +// [0x00002100'00000000, 0x000022ff'ff600000) +// [0x000022ff'ff601000, 0x00002300'00000000) +// [0x000022ff'ff600000, 0x000022ff'ff601000] +// +// We also want to ensure that a wild access by the application into the shadow +// regions will not corrupt our own shadow memory. shadow(shadow) ends up +// disjoint from shadow(app): +// +// [0x00001600'00000000, 0x00001700'00000000) +// [0x00001b00'00000000, 0x00001d00'00000000) +// [0x00001400'00000000, 0x000015ff'ff600000] +// [0x000015ff'ff601000, 0x00001600'00000000] +// [0x000015ff'ff600000, 0x000015ff'ff601000] + +static const struct ApplicationRegion AppRegions[] = { + {0x0000000000000000ull, 0x0000010000000000u, false}, + {0x0000550000000000u, 0x0000570000000000u, false}, + // We make one shadow mapping to hold the shadow regions for all 3 of these + // app regions, as the mappings interleave, and the gap between the 3rd and + // 4th scales down below a page. + {0x00007e0000000000u, 0x00007fffff600000u, false}, + {0x00007fffff601000u, 0x0000800000000000u, true}, + {0xffffffffff600000u, 0xffffffffff601000u, true}, +}; + +#elif SANITIZER_LINUX && SANITIZER_MIPS64 + +// Application memory falls into these 3 regions +// +// [0x00000001'00000000, 0x00000002'00000000) non-PIE + heap +// [0x000000aa'00000000, 0x000000ab'00000000) PIE +// [0x000000ff'00000000, 0x000000ff'ffffffff) libraries + stack +// +// This formula translates from application memory to shadow memory: +// +// shadow(app) = ((app & 0x00000f'ffffffff) + offset) >> scale +// +// Where the offset for 1:1 is 0x000013'00000000. For other scales, the +// offset is shifted left by the scale, except for scales of 1 and 2 where +// it must be tweaked in order to pass the double-shadow test +// (see the "shadow(shadow)" comments below): +// scale == 0: 0x000013'00000000 +// scale == 1: 0x000022'00000000 +// scale == 2: 0x000044'00000000 +// scale >= 3: (0x000013'00000000 << scale) +// +// The resulting shadow memory regions for a 0 scaling are: +// +// [0x00000014'00000000, 0x00000015'00000000) +// [0x0000001d'00000000, 0x0000001e'00000000) +// [0x00000022'00000000, 0x00000022'ffffffff) +// +// We also want to ensure that a wild access by the application into the shadow +// regions will not corrupt our own shadow memory. shadow(shadow) ends up +// disjoint from shadow(app): +// +// [0x00000017'00000000, 0x00000018'00000000) +// [0x00000020'00000000, 0x00000021'00000000) +// [0x00000015'00000000, 0x00000015'ffffffff] + +static const struct ApplicationRegion AppRegions[] = { + {0x0100000000u, 0x0200000000u, false}, + {0xaa00000000u, 0xab00000000u, false}, + {0xff00000000u, 0xffffffffffu, false}, +}; + +#else +static const struct ApplicationRegion AppRegions[] = { + {0x00000000u, 0x1fffffffu, false}, + {0x40000000u, 0xffffffffu, false}, +}; +#endif + +static const u32 NumAppRegions = sizeof(AppRegions) / sizeof(AppRegions[0]); + +// See the comment above: we do not currently support a stack size rlimit +// equal to or larger than 1TB. +#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_MIPS64) +static const uptr MaxStackSize = (1ULL << 40) - 4096; +#else +static const uptr MaxStackSize = (1ULL << 32) - 4096; +#endif + +class ShadowMapping { +public: + // The scale and offset vary by tool. + uptr Scale; + uptr Offset; + + // TODO(sagar.thakur): Try to hardcode the mask as done in the compiler + // instrumentation to reduce the runtime cost of appToShadow. +#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_MIPS64) + struct ShadowMemoryMask40 { + static const uptr Mask = 0x0000000fffffffffu; + }; + struct ShadowMemoryMask47 { + static const uptr Mask = 0x00000fffffffffffu; + }; +#else + struct ShadowMemoryMask32 { + static const uptr Mask = 0x000fffffu; + }; +#endif + + void initialize(uptr ShadowScale) { + +#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_MIPS64) + const uptr OffsetArray40[3] = { + 0x0000001300000000u, + 0x0000002200000000u, + 0x0000004400000000u, + }; + const uptr OffsetArray47[3] = { + 0x0000130000000000u, + 0x0000220000000000u, + 0x0000440000000000u, + }; +#endif + + Scale = ShadowScale; + switch (VmaSize) { +#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_MIPS64) + case 40: { + if (Scale <= 2) + Offset = OffsetArray40[Scale]; + else + Offset = OffsetArray40[0] << Scale; + } break; + case 47: { + if (Scale <= 2) + Offset = OffsetArray47[Scale]; + else + Offset = OffsetArray47[0] << Scale; + } break; +#else + case 32: { + Offset = 0x20000000; // Low Shadow Address. + } break; +#endif + default: { + Printf("ERROR: %d-bit virtual memory address size not supported\n", + VmaSize); + Die(); + } + } + } +}; + +extern ShadowMapping Mapping; + +static inline bool getAppRegion(u32 i, uptr *Start, uptr *End) { + if (i >= NumAppRegions) + return false; + *Start = AppRegions[i].Start; + *End = AppRegions[i].End; + return true; +} + +ALWAYS_INLINE +bool isAppMem(uptr Mem) { + for (u32 i = 0; i < NumAppRegions; ++i) { + if (Mem >= AppRegions[i].Start && Mem < AppRegions[i].End) + return true; + } + return false; +} + +template +uptr appToShadowImpl(uptr App) { +#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_MIPS64) + return (((App & Params::Mask) + Mapping.Offset) >> Mapping.Scale); +#else + return ((App >> Mapping.Scale) + Mapping.Offset); +#endif +} + +ALWAYS_INLINE +uptr appToShadow(uptr App) { + switch (VmaSize) { +#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_MIPS64) + case 40: + return appToShadowImpl(App); + case 47: + return appToShadowImpl(App); +#else + case 32: + return appToShadowImpl(App); +#endif + default: { + Printf("ERROR: %d-bit virtual memory address size not supported\n", + VmaSize); + Die(); + } + } +} + +static inline bool getShadowRegion(u32 i, uptr *Start, uptr *End) { + if (i >= NumAppRegions) + return false; + u32 UnmergedShadowCount = 0; + u32 AppIdx; + for (AppIdx = 0; AppIdx < NumAppRegions; ++AppIdx) { + if (!AppRegions[AppIdx].ShadowMergedWithPrev) { + if (UnmergedShadowCount == i) + break; + UnmergedShadowCount++; + } + } + if (AppIdx >= NumAppRegions || UnmergedShadowCount != i) + return false; + *Start = appToShadow(AppRegions[AppIdx].Start); + // The formula fails for the end itself. + *End = appToShadow(AppRegions[AppIdx].End - 1) + 1; + // Merge with adjacent shadow regions: + for (++AppIdx; AppIdx < NumAppRegions; ++AppIdx) { + if (!AppRegions[AppIdx].ShadowMergedWithPrev) + break; + *Start = Min(*Start, appToShadow(AppRegions[AppIdx].Start)); + *End = Max(*End, appToShadow(AppRegions[AppIdx].End - 1) + 1); + } + return true; +} + +ALWAYS_INLINE +bool isShadowMem(uptr Mem) { + // We assume this is not used on any critical performance path and so there's + // no need to hardcode the mapping results. + for (uptr i = 0; i < NumAppRegions; ++i) { + if (Mem >= appToShadow(AppRegions[i].Start) && + Mem < appToShadow(AppRegions[i].End - 1) + 1) + return true; + } + return false; +} + +} // namespace __esan + +#endif /* ESAN_SHADOW_H */ diff --git a/libsanitizer/esan/esan_sideline.h b/libsanitizer/esan/esan_sideline.h new file mode 100644 index 0000000..aa3fae1 --- /dev/null +++ b/libsanitizer/esan/esan_sideline.h @@ -0,0 +1,61 @@ +//===-- esan_sideline.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Esan sideline thread support. +//===----------------------------------------------------------------------===// + +#ifndef ESAN_SIDELINE_H +#define ESAN_SIDELINE_H + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_internal_defs.h" + +namespace __esan { + +typedef void (*SidelineFunc)(void *Arg); + +// Currently only one sideline thread is supported. +// It calls the SidelineFunc passed to launchThread once on each sample at the +// given frequency in real time (i.e., wall clock time). +class SidelineThread { +public: + // We cannot initialize any fields in the constructor as it will be called + // *after* launchThread for a static instance, as esan.module_ctor is called + // before static initializers. + SidelineThread() {} + ~SidelineThread() {} + + // To simplify declaration in sanitizer code where we want to avoid + // heap allocations, the constructor and destructor do nothing and + // launchThread and joinThread do the real work. + // They should each be called just once. + bool launchThread(SidelineFunc takeSample, void *Arg, u32 FreqMilliSec); + bool joinThread(); + + // Must be called from the sideline thread itself. + bool adjustTimer(u32 FreqMilliSec); + +private: + static int runSideline(void *Arg); + static void registerSignal(int SigNum); + static void handleSidelineSignal(int SigNum, void *SigInfo, void *Ctx); + + char *Stack; + SidelineFunc sampleFunc; + void *FuncArg; + u32 Freq; + uptr SidelineId; + atomic_uintptr_t SidelineExit; +}; + +} // namespace __esan + +#endif // ESAN_SIDELINE_H diff --git a/libsanitizer/esan/esan_sideline_linux.cpp b/libsanitizer/esan/esan_sideline_linux.cpp new file mode 100644 index 0000000..d04f590 --- /dev/null +++ b/libsanitizer/esan/esan_sideline_linux.cpp @@ -0,0 +1,177 @@ +//===-- esan_sideline_linux.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Support for a separate or "sideline" tool thread on Linux. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "esan_sideline.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_linux.h" +#include +#include +#include +#include +#include +#include +#include + +namespace __esan { + +static const int SigAltStackSize = 4*1024; +static const int SidelineStackSize = 4*1024; +static const uptr SidelineIdUninitialized = 1; + +// FIXME: we'll need some kind of TLS (can we trust that a pthread key will +// work in our non-POSIX thread?) to access our data in our signal handler +// with multiple sideline threads. For now we assume there is only one +// sideline thread and we use a dirty solution of a global var. +static SidelineThread *TheThread; + +// We aren't passing SA_NODEFER so the same signal is blocked while here. +void SidelineThread::handleSidelineSignal(int SigNum, void *SigInfo, + void *Ctx) { + VPrintf(3, "Sideline signal %d\n", SigNum); + CHECK_EQ(SigNum, SIGALRM); + // See above about needing TLS to avoid this global var. + SidelineThread *Thread = TheThread; + if (atomic_load(&Thread->SidelineExit, memory_order_relaxed) != 0) + return; + Thread->sampleFunc(Thread->FuncArg); +} + +void SidelineThread::registerSignal(int SigNum) { + __sanitizer_sigaction SigAct; + internal_memset(&SigAct, 0, sizeof(SigAct)); + SigAct.sigaction = handleSidelineSignal; + // We do not pass SA_NODEFER as we want to block the same signal. + SigAct.sa_flags = SA_ONSTACK | SA_SIGINFO; + int Res = internal_sigaction(SigNum, &SigAct, nullptr); + CHECK_EQ(Res, 0); +} + +int SidelineThread::runSideline(void *Arg) { + VPrintf(1, "Sideline thread starting\n"); + SidelineThread *Thread = static_cast(Arg); + + // If the parent dies, we want to exit also. + internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + + // Set up a signal handler on an alternate stack for safety. + InternalScopedBuffer StackMap(SigAltStackSize); + struct sigaltstack SigAltStack; + SigAltStack.ss_sp = StackMap.data(); + SigAltStack.ss_size = SigAltStackSize; + SigAltStack.ss_flags = 0; + internal_sigaltstack(&SigAltStack, nullptr); + + // We inherit the signal mask from the app thread. In case + // we weren't created at init time, we ensure the mask is empty. + __sanitizer_sigset_t SigSet; + internal_sigfillset(&SigSet); + int Res = internal_sigprocmask(SIG_UNBLOCK, &SigSet, nullptr); + CHECK_EQ(Res, 0); + + registerSignal(SIGALRM); + + bool TimerSuccess = Thread->adjustTimer(Thread->Freq); + CHECK(TimerSuccess); + + // We loop, doing nothing but handling itimer signals. + while (atomic_load(&TheThread->SidelineExit, memory_order_relaxed) == 0) + sched_yield(); + + if (!Thread->adjustTimer(0)) + VPrintf(1, "Failed to disable timer\n"); + + VPrintf(1, "Sideline thread exiting\n"); + return 0; +} + +bool SidelineThread::launchThread(SidelineFunc takeSample, void *Arg, + u32 FreqMilliSec) { + // This can only be called once. However, we can't clear a field in + // the constructor and check for that here as the constructor for + // a static instance is called *after* our module_ctor and thus after + // this routine! Thus we rely on the TheThread check below. + CHECK(TheThread == nullptr); // Only one sideline thread is supported. + TheThread = this; + sampleFunc = takeSample; + FuncArg = Arg; + Freq = FreqMilliSec; + atomic_store(&SidelineExit, 0, memory_order_relaxed); + + // We do without a guard page. + Stack = static_cast(MmapOrDie(SidelineStackSize, "SidelineStack")); + // We need to handle the return value from internal_clone() not having been + // assigned yet (for our CHECK in adjustTimer()) so we ensure this has a + // sentinel value. + SidelineId = SidelineIdUninitialized; + // By omitting CLONE_THREAD, the child is in its own thread group and will not + // receive any of the application's signals. + SidelineId = internal_clone( + runSideline, Stack + SidelineStackSize, + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, + this, nullptr /* parent_tidptr */, + nullptr /* newtls */, nullptr /* child_tidptr */); + int ErrCode; + if (internal_iserror(SidelineId, &ErrCode)) { + Printf("FATAL: EfficiencySanitizer failed to spawn a thread (code %d).\n", + ErrCode); + Die(); + return false; // Not reached. + } + return true; +} + +bool SidelineThread::joinThread() { + VPrintf(1, "Joining sideline thread\n"); + bool Res = true; + atomic_store(&SidelineExit, 1, memory_order_relaxed); + while (true) { + uptr Status = internal_waitpid(SidelineId, nullptr, __WALL); + int ErrCode; + if (!internal_iserror(Status, &ErrCode)) + break; + if (ErrCode == EINTR) + continue; + VPrintf(1, "Failed to join sideline thread (errno %d)\n", ErrCode); + Res = false; + break; + } + UnmapOrDie(Stack, SidelineStackSize); + return Res; +} + +// Must be called from the sideline thread itself. +bool SidelineThread::adjustTimer(u32 FreqMilliSec) { + // The return value of internal_clone() may not have been assigned yet: + CHECK(internal_getpid() == SidelineId || + SidelineId == SidelineIdUninitialized); + Freq = FreqMilliSec; + struct itimerval TimerVal; + TimerVal.it_interval.tv_sec = (time_t) Freq / 1000; + TimerVal.it_interval.tv_usec = (time_t) (Freq % 1000) * 1000; + TimerVal.it_value.tv_sec = (time_t) Freq / 1000; + TimerVal.it_value.tv_usec = (time_t) (Freq % 1000) * 1000; + // As we're in a different thread group, we cannot use either + // ITIMER_PROF or ITIMER_VIRTUAL without taking up scheduled + // time ourselves: thus we must use real time. + int Res = setitimer(ITIMER_REAL, &TimerVal, nullptr); + return (Res == 0); +} + +} // namespace __esan + +#endif // SANITIZER_LINUX diff --git a/libsanitizer/esan/libtool-version b/libsanitizer/esan/libtool-version new file mode 100644 index 0000000..9a16cf5 --- /dev/null +++ b/libsanitizer/esan/libtool-version @@ -0,0 +1,6 @@ +# This file is used to maintain libtool version info for libasan. See +# the libtool manual to understand the meaning of the fields. This is +# a separate file so that version updates don't involve re-running +# automake. +# CURRENT:REVISION:AGE +1:0:0 diff --git a/libsanitizer/esan/working_set.cpp b/libsanitizer/esan/working_set.cpp new file mode 100644 index 0000000..3082ead --- /dev/null +++ b/libsanitizer/esan/working_set.cpp @@ -0,0 +1,281 @@ +//===-- working_set.cpp ---------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// This file contains working-set-specific code. +//===----------------------------------------------------------------------===// + +#include "working_set.h" +#include "esan.h" +#include "esan_circular_buffer.h" +#include "esan_flags.h" +#include "esan_shadow.h" +#include "esan_sideline.h" +#include "sanitizer_common/sanitizer_procmaps.h" + +// We shadow every cache line of app memory with one shadow byte. +// - The highest bit of each shadow byte indicates whether the corresponding +// cache line has ever been accessed. +// - The lowest bit of each shadow byte indicates whether the corresponding +// cache line was accessed since the last sample. +// - The other bits are used for working set snapshots at successively +// lower frequencies, each bit to the left from the lowest bit stepping +// down the frequency by 2 to the power of getFlags()->snapshot_step. +// Thus we have something like this: +// Bit 0: Since last sample +// Bit 1: Since last 2^2 samples +// Bit 2: Since last 2^4 samples +// Bit 3: ... +// Bit 7: Ever accessed. +// We live with races in accessing each shadow byte. +typedef unsigned char byte; + +namespace __esan { + +// Our shadow memory assumes that the line size is 64. +static const u32 CacheLineSize = 64; + +// See the shadow byte layout description above. +static const u32 TotalWorkingSetBitIdx = 7; +// We accumulate to the left until we hit this bit. +// We don't need to accumulate to the final bit as it's set on each ref +// by the compiler instrumentation. +static const u32 MaxAccumBitIdx = 6; +static const u32 CurWorkingSetBitIdx = 0; +static const byte ShadowAccessedVal = + (1 << TotalWorkingSetBitIdx) | (1 << CurWorkingSetBitIdx); + +static SidelineThread Thread; +// If we use real-time-based timer samples this won't overflow in any realistic +// scenario, but if we switch to some other unit (such as memory accesses) we +// may want to consider a 64-bit int. +static u32 SnapshotNum; + +// We store the wset size for each of 8 different sampling frequencies. +static const u32 NumFreq = 8; // One for each bit of our shadow bytes. +// We cannot use static objects as the global destructor is called +// prior to our finalize routine. +// These are each circular buffers, sized up front. +CircularBuffer SizePerFreq[NumFreq]; +// We cannot rely on static initializers (they may run too late) but +// we record the size here for clarity: +u32 CircularBufferSizes[NumFreq] = { + // These are each mmap-ed so our minimum is one page. + 32*1024, + 16*1024, + 8*1024, + 4*1024, + 4*1024, + 4*1024, + 4*1024, + 4*1024, +}; + +void processRangeAccessWorkingSet(uptr PC, uptr Addr, SIZE_T Size, + bool IsWrite) { + if (Size == 0) + return; + SIZE_T I = 0; + uptr LineSize = getFlags()->cache_line_size; + // As Addr+Size could overflow at the top of a 32-bit address space, + // we avoid the simpler formula that rounds the start and end. + SIZE_T NumLines = Size / LineSize + + // Add any extra at the start or end adding on an extra line: + (LineSize - 1 + Addr % LineSize + Size % LineSize) / LineSize; + byte *Shadow = (byte *)appToShadow(Addr); + // Write shadow bytes until we're word-aligned. + while (I < NumLines && (uptr)Shadow % 4 != 0) { + if ((*Shadow & ShadowAccessedVal) != ShadowAccessedVal) + *Shadow |= ShadowAccessedVal; + ++Shadow; + ++I; + } + // Write whole shadow words at a time. + // Using a word-stride loop improves the runtime of a microbenchmark of + // memset calls by 10%. + u32 WordValue = ShadowAccessedVal | ShadowAccessedVal << 8 | + ShadowAccessedVal << 16 | ShadowAccessedVal << 24; + while (I + 4 <= NumLines) { + if ((*(u32*)Shadow & WordValue) != WordValue) + *(u32*)Shadow |= WordValue; + Shadow += 4; + I += 4; + } + // Write any trailing shadow bytes. + while (I < NumLines) { + if ((*Shadow & ShadowAccessedVal) != ShadowAccessedVal) + *Shadow |= ShadowAccessedVal; + ++Shadow; + ++I; + } +} + +// This routine will word-align ShadowStart and ShadowEnd prior to scanning. +// It does *not* clear for BitIdx==TotalWorkingSetBitIdx, as that top bit +// measures the access during the entire execution and should never be cleared. +static u32 countAndClearShadowValues(u32 BitIdx, uptr ShadowStart, + uptr ShadowEnd) { + u32 WorkingSetSize = 0; + u32 ByteValue = 0x1 << BitIdx; + u32 WordValue = ByteValue | ByteValue << 8 | ByteValue << 16 | + ByteValue << 24; + // Get word aligned start. + ShadowStart = RoundDownTo(ShadowStart, sizeof(u32)); + bool Accum = getFlags()->record_snapshots && BitIdx < MaxAccumBitIdx; + // Do not clear the bit that measures access during the entire execution. + bool Clear = BitIdx < TotalWorkingSetBitIdx; + for (u32 *Ptr = (u32 *)ShadowStart; Ptr < (u32 *)ShadowEnd; ++Ptr) { + if ((*Ptr & WordValue) != 0) { + byte *BytePtr = (byte *)Ptr; + for (u32 j = 0; j < sizeof(u32); ++j) { + if (BytePtr[j] & ByteValue) { + ++WorkingSetSize; + if (Accum) { + // Accumulate to the lower-frequency bit to the left. + BytePtr[j] |= (ByteValue << 1); + } + } + } + if (Clear) { + // Clear this bit from every shadow byte. + *Ptr &= ~WordValue; + } + } + } + return WorkingSetSize; +} + +// Scan shadow memory to calculate the number of cache lines being accessed, +// i.e., the number of non-zero bits indexed by BitIdx in each shadow byte. +// We also clear the lowest bits (most recent working set snapshot). +// We do *not* clear for BitIdx==TotalWorkingSetBitIdx, as that top bit +// measures the access during the entire execution and should never be cleared. +static u32 computeWorkingSizeAndReset(u32 BitIdx) { + u32 WorkingSetSize = 0; + MemoryMappingLayout MemIter(true/*cache*/); + uptr Start, End, Prot; + while (MemIter.Next(&Start, &End, nullptr/*offs*/, nullptr/*file*/, + 0/*file size*/, &Prot)) { + VPrintf(4, "%s: considering %p-%p app=%d shadow=%d prot=%u\n", + __FUNCTION__, Start, End, Prot, isAppMem(Start), + isShadowMem(Start)); + if (isShadowMem(Start) && (Prot & MemoryMappingLayout::kProtectionWrite)) { + VPrintf(3, "%s: walking %p-%p\n", __FUNCTION__, Start, End); + WorkingSetSize += countAndClearShadowValues(BitIdx, Start, End); + } + } + return WorkingSetSize; +} + +// This is invoked from a signal handler but in a sideline thread doing nothing +// else so it is a little less fragile than a typical signal handler. +static void takeSample(void *Arg) { + u32 BitIdx = CurWorkingSetBitIdx; + u32 Freq = 1; + ++SnapshotNum; // Simpler to skip 0 whose mod matches everything. + while (BitIdx <= MaxAccumBitIdx && (SnapshotNum % Freq) == 0) { + u32 NumLines = computeWorkingSizeAndReset(BitIdx); + VReport(1, "%s: snapshot #%5d bit %d freq %4d: %8u\n", SanitizerToolName, + SnapshotNum, BitIdx, Freq, NumLines); + SizePerFreq[BitIdx].push_back(NumLines); + Freq = Freq << getFlags()->snapshot_step; + BitIdx++; + } +} + +unsigned int getSampleCountWorkingSet() +{ + return SnapshotNum; +} + +// Initialization that must be done before any instrumented code is executed. +void initializeShadowWorkingSet() { + CHECK(getFlags()->cache_line_size == CacheLineSize); +#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_MIPS64) + registerMemoryFaultHandler(); +#endif +} + +void initializeWorkingSet() { + if (getFlags()->record_snapshots) { + for (u32 i = 0; i < NumFreq; ++i) + SizePerFreq[i].initialize(CircularBufferSizes[i]); + Thread.launchThread(takeSample, nullptr, getFlags()->sample_freq); + } +} + +static u32 getPeriodForPrinting(u32 MilliSec, const char *&Unit) { + if (MilliSec > 600000) { + Unit = "min"; + return MilliSec / 60000; + } else if (MilliSec > 10000) { + Unit = "sec"; + return MilliSec / 1000; + } else { + Unit = "ms"; + return MilliSec; + } +} + +static u32 getSizeForPrinting(u32 NumOfCachelines, const char *&Unit) { + // We need a constant to avoid software divide support: + static const u32 KilobyteCachelines = (0x1 << 10) / CacheLineSize; + static const u32 MegabyteCachelines = KilobyteCachelines << 10; + + if (NumOfCachelines > 10 * MegabyteCachelines) { + Unit = "MB"; + return NumOfCachelines / MegabyteCachelines; + } else if (NumOfCachelines > 10 * KilobyteCachelines) { + Unit = "KB"; + return NumOfCachelines / KilobyteCachelines; + } else { + Unit = "Bytes"; + return NumOfCachelines * CacheLineSize; + } +} + +void reportWorkingSet() { + const char *Unit; + if (getFlags()->record_snapshots) { + u32 Freq = 1; + Report(" Total number of samples: %u\n", SnapshotNum); + for (u32 i = 0; i < NumFreq; ++i) { + u32 Time = getPeriodForPrinting(getFlags()->sample_freq*Freq, Unit); + Report(" Samples array #%d at period %u %s\n", i, Time, Unit); + // FIXME: report whether we wrapped around and thus whether we + // have data on the whole run or just the last N samples. + for (u32 j = 0; j < SizePerFreq[i].size(); ++j) { + u32 Size = getSizeForPrinting(SizePerFreq[i][j], Unit); + Report("#%4d: %8u %s (%9u cache lines)\n", j, Size, Unit, + SizePerFreq[i][j]); + } + Freq = Freq << getFlags()->snapshot_step; + } + } + + // Get the working set size for the entire execution. + u32 NumOfCachelines = computeWorkingSizeAndReset(TotalWorkingSetBitIdx); + u32 Size = getSizeForPrinting(NumOfCachelines, Unit); + Report(" %s: the total working set size: %u %s (%u cache lines)\n", + SanitizerToolName, Size, Unit, NumOfCachelines); +} + +int finalizeWorkingSet() { + if (getFlags()->record_snapshots) + Thread.joinThread(); + reportWorkingSet(); + if (getFlags()->record_snapshots) { + for (u32 i = 0; i < NumFreq; ++i) + SizePerFreq[i].free(); + } + return 0; +} + +} // namespace __esan diff --git a/libsanitizer/esan/working_set.h b/libsanitizer/esan/working_set.h new file mode 100644 index 0000000..6a976c3 --- /dev/null +++ b/libsanitizer/esan/working_set.h @@ -0,0 +1,40 @@ +//===-- working_set.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Header for working-set-specific code. +//===----------------------------------------------------------------------===// + +#ifndef WORKING_SET_H +#define WORKING_SET_H + +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_internal_defs.h" + +namespace __esan { + +void initializeWorkingSet(); +void initializeShadowWorkingSet(); +int finalizeWorkingSet(); +void reportWorkingSet(); +unsigned int getSampleCountWorkingSet(); +void processRangeAccessWorkingSet(uptr PC, uptr Addr, SIZE_T Size, + bool IsWrite); + +// Platform-dependent. +void registerMemoryFaultHandler(); +bool processWorkingSetSignal(int SigNum, void (*Handler)(int), + void (**Result)(int)); +bool processWorkingSetSigaction(int SigNum, const void *Act, void *OldAct); +bool processWorkingSetSigprocmask(int How, void *Set, void *OldSet); + +} // namespace __esan + +#endif // WORKING_SET_H diff --git a/libsanitizer/esan/working_set_posix.cpp b/libsanitizer/esan/working_set_posix.cpp new file mode 100644 index 0000000..fcfa871 --- /dev/null +++ b/libsanitizer/esan/working_set_posix.cpp @@ -0,0 +1,133 @@ +//===-- working_set_posix.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// POSIX-specific working set tool code. +//===----------------------------------------------------------------------===// + +#include "working_set.h" +#include "esan_flags.h" +#include "esan_shadow.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_linux.h" +#include +#include + +namespace __esan { + +// We only support regular POSIX threads with a single signal handler +// for the whole process == thread group. +// Thus we only need to store one app signal handler. +// FIXME: Store and use any alternate stack and signal flags set by +// the app. For now we just call the app handler from our handler. +static __sanitizer_sigaction AppSigAct; + +bool processWorkingSetSignal(int SigNum, void (*Handler)(int), + void (**Result)(int)) { + VPrintf(2, "%s: %d\n", __FUNCTION__, SigNum); + if (SigNum == SIGSEGV) { + *Result = AppSigAct.handler; + AppSigAct.sigaction = (void (*)(int, void*, void*))Handler; + return false; // Skip real call. + } + return true; +} + +bool processWorkingSetSigaction(int SigNum, const void *ActVoid, + void *OldActVoid) { + VPrintf(2, "%s: %d\n", __FUNCTION__, SigNum); + if (SigNum == SIGSEGV) { + const struct sigaction *Act = (const struct sigaction *) ActVoid; + struct sigaction *OldAct = (struct sigaction *) OldActVoid; + if (OldAct) + internal_memcpy(OldAct, &AppSigAct, sizeof(OldAct)); + if (Act) + internal_memcpy(&AppSigAct, Act, sizeof(AppSigAct)); + return false; // Skip real call. + } + return true; +} + +bool processWorkingSetSigprocmask(int How, void *Set, void *OldSet) { + VPrintf(2, "%s\n", __FUNCTION__); + // All we need to do is ensure that SIGSEGV is not blocked. + // FIXME: we are not fully transparent as we do not pretend that + // SIGSEGV is still blocked on app queries: that would require + // per-thread mask tracking. + if (Set && (How == SIG_BLOCK || How == SIG_SETMASK)) { + if (internal_sigismember((__sanitizer_sigset_t *)Set, SIGSEGV)) { + VPrintf(1, "%s: removing SIGSEGV from the blocked set\n", __FUNCTION__); + internal_sigdelset((__sanitizer_sigset_t *)Set, SIGSEGV); + } + } + return true; +} + +static void reinstateDefaultHandler(int SigNum) { + __sanitizer_sigaction SigAct; + internal_memset(&SigAct, 0, sizeof(SigAct)); + SigAct.sigaction = (void (*)(int, void*, void*)) SIG_DFL; + int Res = internal_sigaction(SigNum, &SigAct, nullptr); + CHECK(Res == 0); + VPrintf(1, "Unregistered for %d handler\n", SigNum); +} + +// If this is a shadow fault, we handle it here; otherwise, we pass it to the +// app to handle it just as the app would do without our tool in place. +static void handleMemoryFault(int SigNum, void *Info, void *Ctx) { + if (SigNum == SIGSEGV) { + // We rely on si_addr being filled in (thus we do not support old kernels). + siginfo_t *SigInfo = (siginfo_t *)Info; + uptr Addr = (uptr)SigInfo->si_addr; + if (isShadowMem(Addr)) { + VPrintf(3, "Shadow fault @%p\n", Addr); + uptr PageSize = GetPageSizeCached(); + int Res = internal_mprotect((void *)RoundDownTo(Addr, PageSize), + PageSize, PROT_READ|PROT_WRITE); + CHECK(Res == 0); + } else if (AppSigAct.sigaction) { + // FIXME: For simplicity we ignore app options including its signal stack + // (we just use ours) and all the delivery flags. + AppSigAct.sigaction(SigNum, Info, Ctx); + } else { + // Crash instead of spinning with infinite faults. + reinstateDefaultHandler(SigNum); + } + } else + UNREACHABLE("signal not registered"); +} + +void registerMemoryFaultHandler() { + // We do not use an alternate signal stack, as doing so would require + // setting it up for each app thread. + // FIXME: This could result in problems with emulating the app's signal + // handling if the app relies on an alternate stack for SIGSEGV. + + // We require that SIGSEGV is not blocked. We use a sigprocmask + // interceptor to ensure that in the future. Here we ensure it for + // the current thread. We assume there are no other threads at this + // point during initialization, or that at least they do not block + // SIGSEGV. + __sanitizer_sigset_t SigSet; + internal_sigemptyset(&SigSet); + internal_sigprocmask(SIG_BLOCK, &SigSet, nullptr); + + __sanitizer_sigaction SigAct; + internal_memset(&SigAct, 0, sizeof(SigAct)); + SigAct.sigaction = handleMemoryFault; + // We want to handle nested signals b/c we need to handle a + // shadow fault in an app signal handler. + SigAct.sa_flags = SA_SIGINFO | SA_NODEFER; + int Res = internal_sigaction(SIGSEGV, &SigAct, &AppSigAct); + CHECK(Res == 0); + VPrintf(1, "Registered for SIGSEGV handler\n"); +} + +} // namespace __esan diff --git a/libsanitizer/include/esan_interface.h b/libsanitizer/include/esan_interface.h new file mode 100644 index 0000000..c755ed3 --- /dev/null +++ b/libsanitizer/include/esan_interface.h @@ -0,0 +1,50 @@ +//===-- sanitizer/esan_interface.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_ESAN_INTERFACE_H +#define SANITIZER_ESAN_INTERFACE_H + +#include + +// We declare our interface routines as weak to allow the user to avoid +// ifdefs and instead use this pattern to allow building the same sources +// with and without our runtime library: +// if (__esan_report) +// __esan_report(); +#ifdef _MSC_VER +/* selectany is as close to weak as we'll get. */ +#define COMPILER_RT_WEAK __declspec(selectany) +#elif __GNUC__ +#define COMPILER_RT_WEAK __attribute__((weak)) +#else +#define COMPILER_RT_WEAK +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// This function can be called mid-run (or at the end of a run for +// a server process that doesn't shut down normally) to request that +// data for that point in the run be reported from the tool. +void COMPILER_RT_WEAK __esan_report(void); + +// This function returns the number of samples that the esan tool has collected +// to this point. This is useful for testing. +unsigned int COMPILER_RT_WEAK __esan_get_sample_count(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_ESAN_INTERFACE_H diff --git a/libsanitizer/interception/Makefile.in b/libsanitizer/interception/Makefile.in index 96d34e9..be9fab8 100644 --- a/libsanitizer/interception/Makefile.in +++ b/libsanitizer/interception/Makefile.in @@ -216,6 +216,7 @@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ link_libasan = @link_libasan@ +link_libesan = @link_libesan@ link_liblsan = @link_liblsan@ link_libtsan = @link_libtsan@ link_libubsan = @link_libubsan@ diff --git a/libsanitizer/libbacktrace/Makefile.in b/libsanitizer/libbacktrace/Makefile.in index d459403..6d94eed 100644 --- a/libsanitizer/libbacktrace/Makefile.in +++ b/libsanitizer/libbacktrace/Makefile.in @@ -258,6 +258,7 @@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ link_libasan = @link_libasan@ +link_libesan = @link_libesan@ link_liblsan = @link_liblsan@ link_libtsan = @link_libtsan@ link_libubsan = @link_libubsan@ diff --git a/libsanitizer/libsanitizer.spec.in b/libsanitizer/libsanitizer.spec.in index a4fa87d..b747c0c 100644 --- a/libsanitizer/libsanitizer.spec.in +++ b/libsanitizer/libsanitizer.spec.in @@ -9,3 +9,4 @@ *link_liblsan: @link_liblsan@ +*link_libesan: @link_libesan@ diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in index bdd3317..6a77970 100644 --- a/libsanitizer/lsan/Makefile.in +++ b/libsanitizer/lsan/Makefile.in @@ -257,6 +257,7 @@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ link_libasan = @link_libasan@ +link_libesan = @link_libesan@ link_liblsan = @link_liblsan@ link_libtsan = @link_libtsan@ link_libubsan = @link_libubsan@ diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in index 85e4a30..87eb3d3 100644 --- a/libsanitizer/sanitizer_common/Makefile.in +++ b/libsanitizer/sanitizer_common/Makefile.in @@ -261,6 +261,7 @@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ link_libasan = @link_libasan@ +link_libesan = @link_libesan@ link_liblsan = @link_liblsan@ link_libtsan = @link_libtsan@ link_libubsan = @link_libubsan@ diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in index 880629f..6ac52df 100644 --- a/libsanitizer/tsan/Makefile.in +++ b/libsanitizer/tsan/Makefile.in @@ -276,6 +276,7 @@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ link_libasan = @link_libasan@ +link_libesan = @link_libesan@ link_liblsan = @link_liblsan@ link_libtsan = @link_libtsan@ link_libubsan = @link_libubsan@ diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in index 6fb8784..b928efd 100644 --- a/libsanitizer/ubsan/Makefile.in +++ b/libsanitizer/ubsan/Makefile.in @@ -258,6 +258,7 @@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ link_libasan = @link_libasan@ +link_libesan = @link_libesan@ link_liblsan = @link_liblsan@ link_libtsan = @link_libtsan@ link_libubsan = @link_libubsan@ diff --git a/packaging/gcc-aarch64.spec b/packaging/gcc-aarch64.spec index 965c0e8..a04e5f8 100644 --- a/packaging/gcc-aarch64.spec +++ b/packaging/gcc-aarch64.spec @@ -25,6 +25,7 @@ %define quadmath_arch %ix86 x86_64 ia64 %define tsan_arch x86_64 aarch64 +%define esan_arch x86_64 armv7l %define asan_arch x86_64 %ix86 ppc ppc64 %sparc %arm aarch64 %define itm_arch x86_64 %ix86 %arm ppc ppc64 ppc64le s390 s390x %sparc aarch64 %define atomic_arch x86_64 %ix86 %arm aarch64 ppc ppc64 ppc64le s390 s390x %sparc m68k @@ -267,6 +268,16 @@ The runtime library needed to run programs compiled with the %post -n libtsan -p /sbin/ldconfig %postun -n libtsan -p /sbin/ldconfig +%package -n libesan +Summary: The GNU Compiler Efficciency Sanitier Runtime Library +License: MIT +Group: Development/Languages +%description -n libesan +The runtime library needed to run programs compiled with the +-fsanitize=efficiency-working-set option of the GNU Compiler Collection (GCC). +%post -n libesan -p /sbin/ldconfig +%postun -n libesan -p /sbin/ldconfig + %package -n libatomic Summary: The GNU Compiler Atomic Operations Runtime Library License: GPL-3.0-with-GCC-exception @@ -673,6 +684,7 @@ echo "" > gcc/DEV-PHASE RPM_OPT_FLAGS="$RPM_OPT_FLAGS -marm -Wa,-mimplicit-it=arm -fno-omit-frame-pointer" %endif } +%{?esan:%gcc_unforce_options} rm -rf obj mkdir obj cd obj @@ -825,7 +837,7 @@ mv %{buildroot}%{_prefix}/lib/libgcc_s.so* %{buildroot}%{libsubdir}/32/ %endif # move libraries to libdir -for lib in asan atomic cilkrts gfortran gomp cc1 itm lsan quadmath stdc++ supc++ tsan ubsan +for lib in asan atomic cilkrts gfortran gomp cc1 itm lsan quadmath stdc++ supc++ tsan ubsan esan do [ -e %{buildroot}%{libsubdir}/lib$lib.a ] && mv %{buildroot}%{libsubdir}/lib$lib.a %{buildroot}%{libdir}/ [ -e %{buildroot}%{libsubdir}/lib$lib.so ] && mv %{buildroot}%{libsubdir}/lib$lib.so* %{buildroot}%{libdir}/ @@ -890,6 +902,9 @@ mv -v libsanitizer.tar.bz %{buildroot}/src %ifarch %tsan_arch %exclude %{libdir}/libtsan.so %endif +%ifarch %esan_arch +%exclude %{libdir}/libesan.so +%endif %files c++ %defattr(-,root,root) @@ -954,6 +969,14 @@ mv -v libsanitizer.tar.bz %{buildroot}/src %{libdir}/libtsan.so* %endif +%ifarch %esan_arch +%files -n libesan +%manifest gcc.manifest +%license libsanitizer/LICENSE.TXT +%defattr(-,root,root) +%{libdir}/libesan.so* +%endif + %ifarch %atomic_arch %files -n libatomic %manifest gcc.manifest diff --git a/packaging/gcc-armv7hl.spec b/packaging/gcc-armv7hl.spec index 2c05eed..49f6edf 100644 --- a/packaging/gcc-armv7hl.spec +++ b/packaging/gcc-armv7hl.spec @@ -25,6 +25,7 @@ %define quadmath_arch %ix86 x86_64 ia64 %define tsan_arch x86_64 aarch64 +%define esan_arch x86_64 armv7l %define asan_arch x86_64 %ix86 ppc ppc64 %sparc %arm aarch64 %define itm_arch x86_64 %ix86 %arm ppc ppc64 ppc64le s390 s390x %sparc aarch64 %define atomic_arch x86_64 %ix86 %arm aarch64 ppc ppc64 ppc64le s390 s390x %sparc m68k @@ -267,6 +268,16 @@ The runtime library needed to run programs compiled with the %post -n libtsan -p /sbin/ldconfig %postun -n libtsan -p /sbin/ldconfig +%package -n libesan +Summary: The GNU Compiler Efficciency Sanitier Runtime Library +License: MIT +Group: Development/Languages +%description -n libesan +The runtime library needed to run programs compiled with the +-fsanitize=efficiency-working-set option of the GNU Compiler Collection (GCC). +%post -n libesan -p /sbin/ldconfig +%postun -n libesan -p /sbin/ldconfig + %package -n libatomic Summary: The GNU Compiler Atomic Operations Runtime Library License: GPL-3.0-with-GCC-exception @@ -673,6 +684,7 @@ echo "" > gcc/DEV-PHASE RPM_OPT_FLAGS="$RPM_OPT_FLAGS -marm -Wa,-mimplicit-it=arm -fno-omit-frame-pointer" %endif } +%{?esan:%gcc_unforce_options} rm -rf obj mkdir obj cd obj @@ -825,7 +837,7 @@ mv %{buildroot}%{_prefix}/lib/libgcc_s.so* %{buildroot}%{libsubdir}/32/ %endif # move libraries to libdir -for lib in asan atomic cilkrts gfortran gomp cc1 itm lsan quadmath stdc++ supc++ tsan ubsan +for lib in asan atomic cilkrts gfortran gomp cc1 itm lsan quadmath stdc++ supc++ tsan ubsan esan do [ -e %{buildroot}%{libsubdir}/lib$lib.a ] && mv %{buildroot}%{libsubdir}/lib$lib.a %{buildroot}%{libdir}/ [ -e %{buildroot}%{libsubdir}/lib$lib.so ] && mv %{buildroot}%{libsubdir}/lib$lib.so* %{buildroot}%{libdir}/ @@ -890,6 +902,9 @@ mv -v libsanitizer.tar.bz %{buildroot}/src %ifarch %tsan_arch %exclude %{libdir}/libtsan.so %endif +%ifarch %esan_arch +%exclude %{libdir}/libesan.so +%endif %files c++ %defattr(-,root,root) @@ -954,6 +969,14 @@ mv -v libsanitizer.tar.bz %{buildroot}/src %{libdir}/libtsan.so* %endif +%ifarch %esan_arch +%files -n libesan +%manifest gcc.manifest +%license libsanitizer/LICENSE.TXT +%defattr(-,root,root) +%{libdir}/libesan.so* +%endif + %ifarch %atomic_arch %files -n libatomic %manifest gcc.manifest diff --git a/packaging/gcc-armv7l.spec b/packaging/gcc-armv7l.spec index 638e853..95976e3 100644 --- a/packaging/gcc-armv7l.spec +++ b/packaging/gcc-armv7l.spec @@ -25,6 +25,7 @@ %define quadmath_arch %ix86 x86_64 ia64 %define tsan_arch x86_64 aarch64 +%define esan_arch x86_64 armv7l %define asan_arch x86_64 %ix86 ppc ppc64 %sparc %arm aarch64 %define itm_arch x86_64 %ix86 %arm ppc ppc64 ppc64le s390 s390x %sparc aarch64 %define atomic_arch x86_64 %ix86 %arm aarch64 ppc ppc64 ppc64le s390 s390x %sparc m68k @@ -267,6 +268,16 @@ The runtime library needed to run programs compiled with the %post -n libtsan -p /sbin/ldconfig %postun -n libtsan -p /sbin/ldconfig +%package -n libesan +Summary: The GNU Compiler Efficciency Sanitier Runtime Library +License: MIT +Group: Development/Languages +%description -n libesan +The runtime library needed to run programs compiled with the +-fsanitize=efficiency-working-set option of the GNU Compiler Collection (GCC). +%post -n libesan -p /sbin/ldconfig +%postun -n libesan -p /sbin/ldconfig + %package -n libatomic Summary: The GNU Compiler Atomic Operations Runtime Library License: GPL-3.0-with-GCC-exception @@ -673,6 +684,7 @@ echo "" > gcc/DEV-PHASE RPM_OPT_FLAGS="$RPM_OPT_FLAGS -marm -Wa,-mimplicit-it=arm -fno-omit-frame-pointer" %endif } +%{?esan:%gcc_unforce_options} rm -rf obj mkdir obj cd obj @@ -825,7 +837,7 @@ mv %{buildroot}%{_prefix}/lib/libgcc_s.so* %{buildroot}%{libsubdir}/32/ %endif # move libraries to libdir -for lib in asan atomic cilkrts gfortran gomp cc1 itm lsan quadmath stdc++ supc++ tsan ubsan +for lib in asan atomic cilkrts gfortran gomp cc1 itm lsan quadmath stdc++ supc++ tsan ubsan esan do [ -e %{buildroot}%{libsubdir}/lib$lib.a ] && mv %{buildroot}%{libsubdir}/lib$lib.a %{buildroot}%{libdir}/ [ -e %{buildroot}%{libsubdir}/lib$lib.so ] && mv %{buildroot}%{libsubdir}/lib$lib.so* %{buildroot}%{libdir}/ @@ -890,6 +902,9 @@ mv -v libsanitizer.tar.bz %{buildroot}/src %ifarch %tsan_arch %exclude %{libdir}/libtsan.so %endif +%ifarch %esan_arch +%exclude %{libdir}/libesan.so +%endif %files c++ %defattr(-,root,root) @@ -954,6 +969,14 @@ mv -v libsanitizer.tar.bz %{buildroot}/src %{libdir}/libtsan.so* %endif +%ifarch %esan_arch +%files -n libesan +%manifest gcc.manifest +%license libsanitizer/LICENSE.TXT +%defattr(-,root,root) +%{libdir}/libesan.so* +%endif + %ifarch %atomic_arch %files -n libatomic %manifest gcc.manifest diff --git a/packaging/linaro-gcc.spec b/packaging/linaro-gcc.spec index 038f8e2..fd960c5 100644 --- a/packaging/linaro-gcc.spec +++ b/packaging/linaro-gcc.spec @@ -22,6 +22,7 @@ %define quadmath_arch %ix86 x86_64 ia64 %define tsan_arch x86_64 aarch64 +%define esan_arch x86_64 armv7l %define asan_arch x86_64 %ix86 ppc ppc64 %sparc %arm aarch64 %define itm_arch x86_64 %ix86 %arm ppc ppc64 ppc64le s390 s390x %sparc aarch64 %define atomic_arch x86_64 %ix86 %arm aarch64 ppc ppc64 ppc64le s390 s390x %sparc m68k @@ -264,6 +265,16 @@ The runtime library needed to run programs compiled with the %post -n libtsan -p /sbin/ldconfig %postun -n libtsan -p /sbin/ldconfig +%package -n libesan +Summary: The GNU Compiler Efficciency Sanitier Runtime Library +License: MIT +Group: Development/Languages +%description -n libesan +The runtime library needed to run programs compiled with the +-fsanitize=efficiency-working-set option of the GNU Compiler Collection (GCC). +%post -n libesan -p /sbin/ldconfig +%postun -n libesan -p /sbin/ldconfig + %package -n libatomic Summary: The GNU Compiler Atomic Operations Runtime Library License: GPL-3.0-with-GCC-exception @@ -670,6 +681,7 @@ echo "" > gcc/DEV-PHASE RPM_OPT_FLAGS="$RPM_OPT_FLAGS -marm -Wa,-mimplicit-it=arm -fno-omit-frame-pointer" %endif } +%{?esan:%gcc_unforce_options} rm -rf obj mkdir obj cd obj @@ -822,7 +834,7 @@ mv %{buildroot}%{_prefix}/lib/libgcc_s.so* %{buildroot}%{libsubdir}/32/ %endif # move libraries to libdir -for lib in asan atomic cilkrts gfortran gomp cc1 itm lsan quadmath stdc++ supc++ tsan ubsan +for lib in asan atomic cilkrts gfortran gomp cc1 itm lsan quadmath stdc++ supc++ tsan ubsan esan do [ -e %{buildroot}%{libsubdir}/lib$lib.a ] && mv %{buildroot}%{libsubdir}/lib$lib.a %{buildroot}%{libdir}/ [ -e %{buildroot}%{libsubdir}/lib$lib.so ] && mv %{buildroot}%{libsubdir}/lib$lib.so* %{buildroot}%{libdir}/ @@ -887,6 +899,9 @@ mv -v libsanitizer.tar.bz %{buildroot}/src %ifarch %tsan_arch %exclude %{libdir}/libtsan.so %endif +%ifarch %esan_arch +%exclude %{libdir}/libesan.so +%endif %files c++ %defattr(-,root,root) @@ -951,6 +966,14 @@ mv -v libsanitizer.tar.bz %{buildroot}/src %{libdir}/libtsan.so* %endif +%ifarch %esan_arch +%files -n libesan +%manifest gcc.manifest +%license libsanitizer/LICENSE.TXT +%defattr(-,root,root) +%{libdir}/libesan.so* +%endif + %ifarch %atomic_arch %files -n libatomic %manifest gcc.manifest -- 2.7.4 From be2ed3c61077a3d9b333f95c1af7c2f46a3e2ef5 Mon Sep 17 00:00:00 2001 From: Denis Khalikov Date: Wed, 6 Jun 2018 19:40:01 +0300 Subject: [PATCH 03/16] Revert Tizen version of PR sanitizer/77631. Revert commit related to PR sanitizer/77631 implementation for the Tizen GCC. The feature should has an upstream implementation. Change-Id: I1ddf629e229d667877a69d80dd0d6295400b533a --- libbacktrace/ChangeLog | 39 -- libbacktrace/Makefile.am | 155 +------ libbacktrace/Makefile.in | 401 +++++------------ libbacktrace/configure | 43 +- libbacktrace/configure.ac | 2 - libbacktrace/crc32.c | 107 ----- libbacktrace/elf.c | 1058 +++++---------------------------------------- 7 files changed, 208 insertions(+), 1597 deletions(-) delete mode 100644 libbacktrace/crc32.c diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog index c2546be..4cb639b 100644 --- a/libbacktrace/ChangeLog +++ b/libbacktrace/ChangeLog @@ -1,42 +1,3 @@ -2017-07-29 Denis Khalikov - - PR sanitizer/77631 - * Makefile.am: Updated. - * configure.ac: Updated. - * configure: Regenerated. - * Makefile.in: Regenerated. - * elf.c (enum type_of_file): New enum. - (enum type_of_elf): New enum. - (enum debug_path): New enum. - (get_uint32): New function. - (get_crc32): New function. - (base_name_len): New function. - (check_sum): New function. Verify sum. - (process_elf_header): New function. Process elf header. - (elf_get_section_by_name): New function. Get section by name. - (backtrace_readlink): New function. Get type of file from filename. - (resolve_realname): New function. Resolve real name if file is link. - (backtrace_resolve_realname): New function. Resolve real name for any - file type. - (search_for_debugfile): New function. Search for debug file in known - paths. - (open_debugfile_by_gnulink): New function. Open debug file with - gnulink. - (hex): New function. Convert to hex. - (get_build_id_name): New function. Generate build-id name. - (open_debugfile_by_build_id): New function. Open debug file with - build-id. - (backtrace_open_debugfile): New function. Open debug file. - (get_exec_filename): New function. Get pathname of the executable. - (elf_add): Move code which reads elf header, headers section and names - section to process_elf_header. - Call backtrace_open_debugfile_file for executable. - (phdr_callback): Call backtrace_open_debugfile function for shared - library. - * crc32.c: New file. - (gnu_debuglink_crc32): New function. Generate crc32 sum. - - 2016-12-21 Release Manager * GCC 6.3.0 released. diff --git a/libbacktrace/Makefile.am b/libbacktrace/Makefile.am index 61f1804..a7df025 100644 --- a/libbacktrace/Makefile.am +++ b/libbacktrace/Makefile.am @@ -100,161 +100,8 @@ stest_LDADD = libbacktrace.la check_PROGRAMS += stest -glinktest_SOURCES = btest.c -glinktest_CFLAGS = $(AM_CFLAGS) -g -O -glinktest_LDADD = libbacktrace.la - -check_PROGRAMS += glinktest - -bidtest_SOURCES = btest.c -bidtest_CFLAGS = $(AM_CFLAGS) -g -O -Wl,--build-id=0x0123456789abcdef0123456789abcdef01234567 -bidtest_LDADD = libbacktrace.la - -check_PROGRAMS += bidtest - -pictest_SOURCES = btest.c -pictest_CFLAGS = $(AM_CFLAGS) -g -O -fPIC -pie -pictest_LDADD = libbacktrace.la - -check_PROGRAMS += pictest - -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if ! test -e glinktest.debug; then \ - $(OBJCOPY) --only-keep-debug glinktest glinktest.debug; \ - $(STRIP) glinktest; \ - $(OBJCOPY) --add-gnu-debuglink=glinktest.debug glinktest; \ - fi; \ - if ! test -e pictest.debug; then \ - $(OBJCOPY) --only-keep-debug pictest pictest.debug; \ - $(STRIP) pictest; \ - $(OBJCOPY) --add-gnu-debuglink=pictest.debug pictest; \ - fi; \ - if ! test -e .build-id; then \ - mkdir .build-id; \ - mkdir .build-id/01/; \ - mkdir temp; \ - $(OBJCOPY) --only-keep-debug bidtest bidtest.debug; \ - $(STRIP) bidtest; \ - touch temp; \ - mv bidtest.debug temp;\ - ln -s ../../temp/bidtest.debug .build-id/01/23456789abcdef0123456789abcdef01234567.debug; \ - fi; \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ - fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ - else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ - fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ - else \ - skipped="($$skip tests were not run)"; \ - fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi - - -clean-checkPROGRAMS: - @if test -e glinktest.debug; then \ - echo "rm -f glinktest.debug pictest.debug"; \ - rm -f glinktest.debug pictest.debug; \ - fi; \ - if test -d temp; then \ - echo "rm -rf temp"; \ - rm -rf temp; \ - fi; \ - if test -d .build-id; then \ - echo "rm -rf .build-id"; \ - rm -rf .build-id; \ - fi; \ - list='$(check_PROGRAMS)'; \ - test -n "$$list" || exit 0; \ - echo " rm -f" $$list; \ - rm -f $$list || exit $$?; \ - test -n "$(EXEEXT)" || exit 0; \ - list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f" $$list; \ - rm -f $$list; - endif NATIVE + # We can't use automake's automatic dependency tracking, because it # breaks when using bootstrap-lean. Automatic dependency tracking # with GCC bootstrap will cause some of the objects to depend on diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in index 1f51c4e..586b6a6 100644 --- a/libbacktrace/Makefile.in +++ b/libbacktrace/Makefile.in @@ -16,7 +16,7 @@ @SET_MAKE@ # Makefile.am -- Backtrace Makefile. -# Copyright (C) 2012-2016 Free Software Foundation, Inc. +# Copyright (C) 2012-2015 Free Software Foundation, Inc. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -84,7 +84,7 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ check_PROGRAMS = $(am__EXEEXT_1) -@NATIVE_TRUE@am__append_1 = btest stest glinktest bidtest pictest +@NATIVE_TRUE@am__append_1 = btest stest subdir = . DIST_COMMON = README ChangeLog $(srcdir)/Makefile.in \ $(srcdir)/Makefile.am $(top_srcdir)/configure \ @@ -113,33 +113,13 @@ am__DEPENDENCIES_1 = am_libbacktrace_la_OBJECTS = atomic.lo dwarf.lo fileline.lo posix.lo \ print.lo sort.lo state.lo libbacktrace_la_OBJECTS = $(am_libbacktrace_la_OBJECTS) -@NATIVE_TRUE@am__EXEEXT_1 = btest$(EXEEXT) stest$(EXEEXT) \ -@NATIVE_TRUE@ glinktest$(EXEEXT) bidtest$(EXEEXT) \ -@NATIVE_TRUE@ pictest$(EXEEXT) -@NATIVE_TRUE@am_bidtest_OBJECTS = bidtest-btest.$(OBJEXT) -bidtest_OBJECTS = $(am_bidtest_OBJECTS) -@NATIVE_TRUE@bidtest_DEPENDENCIES = libbacktrace.la -bidtest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CCLD) $(bidtest_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ +@NATIVE_TRUE@am__EXEEXT_1 = btest$(EXEEXT) stest$(EXEEXT) @NATIVE_TRUE@am_btest_OBJECTS = btest-btest.$(OBJEXT) btest_OBJECTS = $(am_btest_OBJECTS) @NATIVE_TRUE@btest_DEPENDENCIES = libbacktrace.la btest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(btest_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ -@NATIVE_TRUE@am_glinktest_OBJECTS = glinktest-btest.$(OBJEXT) -glinktest_OBJECTS = $(am_glinktest_OBJECTS) -@NATIVE_TRUE@glinktest_DEPENDENCIES = libbacktrace.la -glinktest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(glinktest_CFLAGS) \ - $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -@NATIVE_TRUE@am_pictest_OBJECTS = pictest-btest.$(OBJEXT) -pictest_OBJECTS = $(am_pictest_OBJECTS) -@NATIVE_TRUE@pictest_DEPENDENCIES = libbacktrace.la -pictest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CCLD) $(pictest_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ @NATIVE_TRUE@am_stest_OBJECTS = stest.$(OBJEXT) stest_OBJECTS = $(am_stest_OBJECTS) @NATIVE_TRUE@stest_DEPENDENCIES = libbacktrace.la @@ -156,8 +136,7 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ SOURCES = $(libbacktrace_la_SOURCES) $(EXTRA_libbacktrace_la_SOURCES) \ - $(bidtest_SOURCES) $(btest_SOURCES) $(glinktest_SOURCES) \ - $(pictest_SOURCES) $(stest_SOURCES) + $(btest_SOURCES) $(stest_SOURCES) MULTISRCTOP = MULTIBUILDTOP = MULTIDIRS = @@ -221,7 +200,6 @@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ -OBJCOPY = @OBJCOPY@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ @@ -352,15 +330,6 @@ TESTS = $(check_PROGRAMS) @NATIVE_TRUE@btest_LDADD = libbacktrace.la @NATIVE_TRUE@stest_SOURCES = stest.c @NATIVE_TRUE@stest_LDADD = libbacktrace.la -@NATIVE_TRUE@glinktest_SOURCES = btest.c -@NATIVE_TRUE@glinktest_CFLAGS = $(AM_CFLAGS) -g -O -@NATIVE_TRUE@glinktest_LDADD = libbacktrace.la -@NATIVE_TRUE@bidtest_SOURCES = btest.c -@NATIVE_TRUE@bidtest_CFLAGS = $(AM_CFLAGS) -g -O -Wl,--build-id=0x0123456789abcdef0123456789abcdef01234567 -@NATIVE_TRUE@bidtest_LDADD = libbacktrace.la -@NATIVE_TRUE@pictest_SOURCES = btest.c -@NATIVE_TRUE@pictest_CFLAGS = $(AM_CFLAGS) -g -O -fPIC -pie -@NATIVE_TRUE@pictest_LDADD = libbacktrace.la # We can't use automake's automatic dependency tracking, because it # breaks when using bootstrap-lean. Automatic dependency tracking @@ -442,26 +411,17 @@ clean-noinstLTLIBRARIES: libbacktrace.la: $(libbacktrace_la_OBJECTS) $(libbacktrace_la_DEPENDENCIES) $(EXTRA_libbacktrace_la_DEPENDENCIES) $(LINK) $(libbacktrace_la_OBJECTS) $(libbacktrace_la_LIBADD) $(LIBS) -@NATIVE_FALSE@clean-checkPROGRAMS: -@NATIVE_FALSE@ @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ -@NATIVE_FALSE@ echo " rm -f" $$list; \ -@NATIVE_FALSE@ rm -f $$list || exit $$?; \ -@NATIVE_FALSE@ test -n "$(EXEEXT)" || exit 0; \ -@NATIVE_FALSE@ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ -@NATIVE_FALSE@ echo " rm -f" $$list; \ -@NATIVE_FALSE@ rm -f $$list -bidtest$(EXEEXT): $(bidtest_OBJECTS) $(bidtest_DEPENDENCIES) $(EXTRA_bidtest_DEPENDENCIES) - @rm -f bidtest$(EXEEXT) - $(bidtest_LINK) $(bidtest_OBJECTS) $(bidtest_LDADD) $(LIBS) +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list btest$(EXEEXT): $(btest_OBJECTS) $(btest_DEPENDENCIES) $(EXTRA_btest_DEPENDENCIES) @rm -f btest$(EXEEXT) $(btest_LINK) $(btest_OBJECTS) $(btest_LDADD) $(LIBS) -glinktest$(EXEEXT): $(glinktest_OBJECTS) $(glinktest_DEPENDENCIES) $(EXTRA_glinktest_DEPENDENCIES) - @rm -f glinktest$(EXEEXT) - $(glinktest_LINK) $(glinktest_OBJECTS) $(glinktest_LDADD) $(LIBS) -pictest$(EXEEXT): $(pictest_OBJECTS) $(pictest_DEPENDENCIES) $(EXTRA_pictest_DEPENDENCIES) - @rm -f pictest$(EXEEXT) - $(pictest_LINK) $(pictest_OBJECTS) $(pictest_LDADD) $(LIBS) stest$(EXEEXT): $(stest_OBJECTS) $(stest_DEPENDENCIES) $(EXTRA_stest_DEPENDENCIES) @rm -f stest$(EXEEXT) $(LINK) $(stest_OBJECTS) $(stest_LDADD) $(LIBS) @@ -481,30 +441,12 @@ distclean-compile: .c.lo: $(LTCOMPILE) -c -o $@ $< -bidtest-btest.o: btest.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bidtest_CFLAGS) $(CFLAGS) -c -o bidtest-btest.o `test -f 'btest.c' || echo '$(srcdir)/'`btest.c - -bidtest-btest.obj: btest.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bidtest_CFLAGS) $(CFLAGS) -c -o bidtest-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi` - btest-btest.o: btest.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(btest_CFLAGS) $(CFLAGS) -c -o btest-btest.o `test -f 'btest.c' || echo '$(srcdir)/'`btest.c btest-btest.obj: btest.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(btest_CFLAGS) $(CFLAGS) -c -o btest-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi` -glinktest-btest.o: btest.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(glinktest_CFLAGS) $(CFLAGS) -c -o glinktest-btest.o `test -f 'btest.c' || echo '$(srcdir)/'`btest.c - -glinktest-btest.obj: btest.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(glinktest_CFLAGS) $(CFLAGS) -c -o glinktest-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi` - -pictest-btest.o: btest.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pictest_CFLAGS) $(CFLAGS) -c -o pictest-btest.o `test -f 'btest.c' || echo '$(srcdir)/'`btest.c - -pictest-btest.obj: btest.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pictest_CFLAGS) $(CFLAGS) -c -o pictest-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi` - mostlyclean-libtool: -rm -f *.lo @@ -583,98 +525,98 @@ GTAGS: distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -@NATIVE_FALSE@check-TESTS: $(TESTS) -@NATIVE_FALSE@ @failed=0; all=0; xfail=0; xpass=0; skip=0; \ -@NATIVE_FALSE@ srcdir=$(srcdir); export srcdir; \ -@NATIVE_FALSE@ list=' $(TESTS) '; \ -@NATIVE_FALSE@ $(am__tty_colors); \ -@NATIVE_FALSE@ if test -n "$$list"; then \ -@NATIVE_FALSE@ for tst in $$list; do \ -@NATIVE_FALSE@ if test -f ./$$tst; then dir=./; \ -@NATIVE_FALSE@ elif test -f $$tst; then dir=; \ -@NATIVE_FALSE@ else dir="$(srcdir)/"; fi; \ -@NATIVE_FALSE@ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ -@NATIVE_FALSE@ all=`expr $$all + 1`; \ -@NATIVE_FALSE@ case " $(XFAIL_TESTS) " in \ -@NATIVE_FALSE@ *[\ \ ]$$tst[\ \ ]*) \ -@NATIVE_FALSE@ xpass=`expr $$xpass + 1`; \ -@NATIVE_FALSE@ failed=`expr $$failed + 1`; \ -@NATIVE_FALSE@ col=$$red; res=XPASS; \ -@NATIVE_FALSE@ ;; \ -@NATIVE_FALSE@ *) \ -@NATIVE_FALSE@ col=$$grn; res=PASS; \ -@NATIVE_FALSE@ ;; \ -@NATIVE_FALSE@ esac; \ -@NATIVE_FALSE@ elif test $$? -ne 77; then \ -@NATIVE_FALSE@ all=`expr $$all + 1`; \ -@NATIVE_FALSE@ case " $(XFAIL_TESTS) " in \ -@NATIVE_FALSE@ *[\ \ ]$$tst[\ \ ]*) \ -@NATIVE_FALSE@ xfail=`expr $$xfail + 1`; \ -@NATIVE_FALSE@ col=$$lgn; res=XFAIL; \ -@NATIVE_FALSE@ ;; \ -@NATIVE_FALSE@ *) \ -@NATIVE_FALSE@ failed=`expr $$failed + 1`; \ -@NATIVE_FALSE@ col=$$red; res=FAIL; \ -@NATIVE_FALSE@ ;; \ -@NATIVE_FALSE@ esac; \ -@NATIVE_FALSE@ else \ -@NATIVE_FALSE@ skip=`expr $$skip + 1`; \ -@NATIVE_FALSE@ col=$$blu; res=SKIP; \ -@NATIVE_FALSE@ fi; \ -@NATIVE_FALSE@ echo "$${col}$$res$${std}: $$tst"; \ -@NATIVE_FALSE@ done; \ -@NATIVE_FALSE@ if test "$$all" -eq 1; then \ -@NATIVE_FALSE@ tests="test"; \ -@NATIVE_FALSE@ All=""; \ -@NATIVE_FALSE@ else \ -@NATIVE_FALSE@ tests="tests"; \ -@NATIVE_FALSE@ All="All "; \ -@NATIVE_FALSE@ fi; \ -@NATIVE_FALSE@ if test "$$failed" -eq 0; then \ -@NATIVE_FALSE@ if test "$$xfail" -eq 0; then \ -@NATIVE_FALSE@ banner="$$All$$all $$tests passed"; \ -@NATIVE_FALSE@ else \ -@NATIVE_FALSE@ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ -@NATIVE_FALSE@ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ -@NATIVE_FALSE@ fi; \ -@NATIVE_FALSE@ else \ -@NATIVE_FALSE@ if test "$$xpass" -eq 0; then \ -@NATIVE_FALSE@ banner="$$failed of $$all $$tests failed"; \ -@NATIVE_FALSE@ else \ -@NATIVE_FALSE@ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ -@NATIVE_FALSE@ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ -@NATIVE_FALSE@ fi; \ -@NATIVE_FALSE@ fi; \ -@NATIVE_FALSE@ dashes="$$banner"; \ -@NATIVE_FALSE@ skipped=""; \ -@NATIVE_FALSE@ if test "$$skip" -ne 0; then \ -@NATIVE_FALSE@ if test "$$skip" -eq 1; then \ -@NATIVE_FALSE@ skipped="($$skip test was not run)"; \ -@NATIVE_FALSE@ else \ -@NATIVE_FALSE@ skipped="($$skip tests were not run)"; \ -@NATIVE_FALSE@ fi; \ -@NATIVE_FALSE@ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ -@NATIVE_FALSE@ dashes="$$skipped"; \ -@NATIVE_FALSE@ fi; \ -@NATIVE_FALSE@ report=""; \ -@NATIVE_FALSE@ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ -@NATIVE_FALSE@ report="Please report to $(PACKAGE_BUGREPORT)"; \ -@NATIVE_FALSE@ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ -@NATIVE_FALSE@ dashes="$$report"; \ -@NATIVE_FALSE@ fi; \ -@NATIVE_FALSE@ dashes=`echo "$$dashes" | sed s/./=/g`; \ -@NATIVE_FALSE@ if test "$$failed" -eq 0; then \ -@NATIVE_FALSE@ col="$$grn"; \ -@NATIVE_FALSE@ else \ -@NATIVE_FALSE@ col="$$red"; \ -@NATIVE_FALSE@ fi; \ -@NATIVE_FALSE@ echo "$${col}$$dashes$${std}"; \ -@NATIVE_FALSE@ echo "$${col}$$banner$${std}"; \ -@NATIVE_FALSE@ test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ -@NATIVE_FALSE@ test -z "$$report" || echo "$${col}$$report$${std}"; \ -@NATIVE_FALSE@ echo "$${col}$$dashes$${std}"; \ -@NATIVE_FALSE@ test "$$failed" -eq 0; \ -@NATIVE_FALSE@ else :; fi +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS @@ -803,141 +745,6 @@ uninstall-am: mostlyclean-multi pdf pdf-am ps ps-am tags uninstall \ uninstall-am - -@NATIVE_TRUE@check-TESTS: $(TESTS) -@NATIVE_TRUE@ @failed=0; all=0; xfail=0; xpass=0; skip=0; \ -@NATIVE_TRUE@ srcdir=$(srcdir); export srcdir; \ -@NATIVE_TRUE@ list=' $(TESTS) '; \ -@NATIVE_TRUE@ $(am__tty_colors); \ -@NATIVE_TRUE@ if ! test -e glinktest.debug; then \ -@NATIVE_TRUE@ $(OBJCOPY) --only-keep-debug glinktest glinktest.debug; \ -@NATIVE_TRUE@ $(STRIP) glinktest; \ -@NATIVE_TRUE@ $(OBJCOPY) --add-gnu-debuglink=glinktest.debug glinktest; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ if ! test -e pictest.debug; then \ -@NATIVE_TRUE@ $(OBJCOPY) --only-keep-debug pictest pictest.debug; \ -@NATIVE_TRUE@ $(STRIP) pictest; \ -@NATIVE_TRUE@ $(OBJCOPY) --add-gnu-debuglink=pictest.debug pictest; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ if ! test -e .build-id; then \ -@NATIVE_TRUE@ mkdir .build-id; \ -@NATIVE_TRUE@ mkdir .build-id/01/; \ -@NATIVE_TRUE@ mkdir temp; \ -@NATIVE_TRUE@ $(OBJCOPY) --only-keep-debug bidtest bidtest.debug; \ -@NATIVE_TRUE@ $(STRIP) bidtest; \ -@NATIVE_TRUE@ touch temp; \ -@NATIVE_TRUE@ mv bidtest.debug temp;\ -@NATIVE_TRUE@ ln -s ../../temp/bidtest.debug .build-id/01/23456789abcdef0123456789abcdef01234567.debug; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ if test -n "$$list"; then \ -@NATIVE_TRUE@ for tst in $$list; do \ -@NATIVE_TRUE@ if test -f ./$$tst; then dir=./; \ -@NATIVE_TRUE@ elif test -f $$tst; then dir=; \ -@NATIVE_TRUE@ else dir="$(srcdir)/"; fi; \ -@NATIVE_TRUE@ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ -@NATIVE_TRUE@ all=`expr $$all + 1`; \ -@NATIVE_TRUE@ case " $(XFAIL_TESTS) " in \ -@NATIVE_TRUE@ *[\ \ ]$$tst[\ \ ]*) \ -@NATIVE_TRUE@ xpass=`expr $$xpass + 1`; \ -@NATIVE_TRUE@ failed=`expr $$failed + 1`; \ -@NATIVE_TRUE@ col=$$red; res=XPASS; \ -@NATIVE_TRUE@ ;; \ -@NATIVE_TRUE@ *) \ -@NATIVE_TRUE@ col=$$grn; res=PASS; \ -@NATIVE_TRUE@ ;; \ -@NATIVE_TRUE@ esac; \ -@NATIVE_TRUE@ elif test $$? -ne 77; then \ -@NATIVE_TRUE@ all=`expr $$all + 1`; \ -@NATIVE_TRUE@ case " $(XFAIL_TESTS) " in \ -@NATIVE_TRUE@ *[\ \ ]$$tst[\ \ ]*) \ -@NATIVE_TRUE@ xfail=`expr $$xfail + 1`; \ -@NATIVE_TRUE@ col=$$lgn; res=XFAIL; \ -@NATIVE_TRUE@ ;; \ -@NATIVE_TRUE@ *) \ -@NATIVE_TRUE@ failed=`expr $$failed + 1`; \ -@NATIVE_TRUE@ col=$$red; res=FAIL; \ -@NATIVE_TRUE@ ;; \ -@NATIVE_TRUE@ esac; \ -@NATIVE_TRUE@ else \ -@NATIVE_TRUE@ skip=`expr $$skip + 1`; \ -@NATIVE_TRUE@ col=$$blu; res=SKIP; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ echo "$${col}$$res$${std}: $$tst"; \ -@NATIVE_TRUE@ done; \ -@NATIVE_TRUE@ if test "$$all" -eq 1; then \ -@NATIVE_TRUE@ tests="test"; \ -@NATIVE_TRUE@ All=""; \ -@NATIVE_TRUE@ else \ -@NATIVE_TRUE@ tests="tests"; \ -@NATIVE_TRUE@ All="All "; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ if test "$$failed" -eq 0; then \ -@NATIVE_TRUE@ if test "$$xfail" -eq 0; then \ -@NATIVE_TRUE@ banner="$$All$$all $$tests passed"; \ -@NATIVE_TRUE@ else \ -@NATIVE_TRUE@ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ -@NATIVE_TRUE@ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ else \ -@NATIVE_TRUE@ if test "$$xpass" -eq 0; then \ -@NATIVE_TRUE@ banner="$$failed of $$all $$tests failed"; \ -@NATIVE_TRUE@ else \ -@NATIVE_TRUE@ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ -@NATIVE_TRUE@ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ dashes="$$banner"; \ -@NATIVE_TRUE@ skipped=""; \ -@NATIVE_TRUE@ if test "$$skip" -ne 0; then \ -@NATIVE_TRUE@ if test "$$skip" -eq 1; then \ -@NATIVE_TRUE@ skipped="($$skip test was not run)"; \ -@NATIVE_TRUE@ else \ -@NATIVE_TRUE@ skipped="($$skip tests were not run)"; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ -@NATIVE_TRUE@ dashes="$$skipped"; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ report=""; \ -@NATIVE_TRUE@ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ -@NATIVE_TRUE@ report="Please report to $(PACKAGE_BUGREPORT)"; \ -@NATIVE_TRUE@ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ -@NATIVE_TRUE@ dashes="$$report"; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ dashes=`echo "$$dashes" | sed s/./=/g`; \ -@NATIVE_TRUE@ if test "$$failed" -eq 0; then \ -@NATIVE_TRUE@ col="$$grn"; \ -@NATIVE_TRUE@ else \ -@NATIVE_TRUE@ col="$$red"; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ echo "$${col}$$dashes$${std}"; \ -@NATIVE_TRUE@ echo "$${col}$$banner$${std}"; \ -@NATIVE_TRUE@ test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ -@NATIVE_TRUE@ test -z "$$report" || echo "$${col}$$report$${std}"; \ -@NATIVE_TRUE@ echo "$${col}$$dashes$${std}"; \ -@NATIVE_TRUE@ test "$$failed" -eq 0; \ -@NATIVE_TRUE@ else :; fi - -@NATIVE_TRUE@clean-checkPROGRAMS: -@NATIVE_TRUE@ @if test -e glinktest.debug; then \ -@NATIVE_TRUE@ echo "rm -f glinktest.debug pictest.debug"; \ -@NATIVE_TRUE@ rm -f glinktest.debug pictest.debug; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ if test -d temp; then \ -@NATIVE_TRUE@ echo "rm -rf temp"; \ -@NATIVE_TRUE@ rm -rf temp; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ if test -d .build-id; then \ -@NATIVE_TRUE@ echo "rm -rf .build-id"; \ -@NATIVE_TRUE@ rm -rf .build-id; \ -@NATIVE_TRUE@ fi; \ -@NATIVE_TRUE@ list='$(check_PROGRAMS)'; \ -@NATIVE_TRUE@ test -n "$$list" || exit 0; \ -@NATIVE_TRUE@ echo " rm -f" $$list; \ -@NATIVE_TRUE@ rm -f $$list || exit $$?; \ -@NATIVE_TRUE@ test -n "$(EXEEXT)" || exit 0; \ -@NATIVE_TRUE@ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ -@NATIVE_TRUE@ echo " rm -f" $$list; \ -@NATIVE_TRUE@ rm -f $$list; alloc.lo: config.h backtrace.h internal.h backtrace.lo: config.h backtrace.h internal.h btest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h diff --git a/libbacktrace/configure b/libbacktrace/configure index 8910996..4e720e1 100755 --- a/libbacktrace/configure +++ b/libbacktrace/configure @@ -630,7 +630,6 @@ LD FGREP SED LIBTOOL -OBJCOPY RANLIB MAINT MAINTAINER_MODE_FALSE @@ -5012,44 +5011,6 @@ else fi -# Extract the first word of "objcopy", so it can be a program name with args. -set dummy objcopy; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_OBJCOPY+set}" = set; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$OBJCOPY"; then - ac_cv_prog_OBJCOPY="$OBJCOPY" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_OBJCOPY="objcopy" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -OBJCOPY=$ac_cv_prog_OBJCOPY -if test -n "$OBJCOPY"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJCOPY" >&5 -$as_echo "$OBJCOPY" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. @@ -11170,7 +11131,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11173 "configure" +#line 11134 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11276,7 +11237,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11279 "configure" +#line 11240 "configure" #include "confdefs.h" #if HAVE_DLFCN_H diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac index bc4e688..71e8518 100644 --- a/libbacktrace/configure.ac +++ b/libbacktrace/configure.ac @@ -74,8 +74,6 @@ AC_SUBST(CFLAGS) AC_PROG_RANLIB -AC_CHECK_PROG(OBJCOPY, objcopy, objcopy) - AC_PROG_AWK case "$AWK" in "") AC_MSG_ERROR([can't build without awk]) ;; diff --git a/libbacktrace/crc32.c b/libbacktrace/crc32.c deleted file mode 100644 index f38a422..0000000 --- a/libbacktrace/crc32.c +++ /dev/null @@ -1,107 +0,0 @@ -/* crc32.c -- compute the CRC-32 - - The CRC-32 defined in IEEE 802.3 using the polynomial: - - x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 - + x2 + x + 1 - - Hexademical representation of the polynomial over GF(2): 0xedb88320 - - The table was generated by algorithm from zlib.(zlib/crc32.c) - The algorithm is described below. - - * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - - Polynomials over GF(2) are represented in binary, one bit per coefficient, - with the lowest powers in the most significant bit. Then adding polynomials - is just exclusive-or, and multiplying a polynomial by x is a right shift by - one. - - This calculation is done using the shift-register method of multiplying and - taking the remainder. The register is initialized to zero, and for each - incoming bit, x^32 is added mod p to the register if the bit is a one (where - x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by - x (which is shifting right by one and adding x^32 mod p if the bit shifted - out is a one). We start with the highest power (least significant bit) of - q and repeat for all eight bits of q. - - unsigned long crc_table[256]; - void make_crc_table() - { - unsigned long c; - int n, k; - unsigned long poly; - static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; - - poly = 0; - for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) - poly |= (unsigned long)1 << (31 - p[n]); - - for (n = 0; n < 256; n++) { - c = (unsigned long)n; - for (k = 0; k < 8; k++) - c = c & 1 ? poly ^ (c >> 1) : c >> 1; - crc_table[n] = c; - } - } -*/ - -static const uint32_t crc32_table[256] - = {0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; - -static uint32_t -gnu_debuglink_crc32 (uint32_t crc, const unsigned char *buf, size_t len) -{ - const unsigned char *end; - end = buf + len; - crc = ~crc; - while (buf < end) - { - crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); - ++buf; - } - return ~crc; -} diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c index ec68737..81ba344 100644 --- a/libbacktrace/elf.c +++ b/libbacktrace/elf.c @@ -35,9 +35,6 @@ POSSIBILITY OF SUCH DAMAGE. */ #include #include #include -#include -#include -#include #ifdef HAVE_DL_ITERATE_PHDR #include @@ -45,24 +42,6 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "backtrace.h" #include "internal.h" -#include "filenames.h" -/* In case some other libraries build libbacktrace from source - (for example: libsanitizer does that way), we should manually update - each Makefile for each library to avoid situation with undefined - references after adding new file into libbacktrace sources. - Another way is to include crc32.c file into preproccessing stage. */ -#include "crc32.c" - -#undef PATH_MAX -#define PATH_MAX 4096 - -#ifndef DIR_SEPARATOR -#define DIR_SEPARATOR '/' -#endif - -#ifndef HAVE_GETEXECNAME -#define getexecname() NULL -#endif #ifndef HAVE_DL_ITERATE_PHDR @@ -304,853 +283,6 @@ struct elf_syminfo_data size_t count; }; -/* Information to read ELF note section. */ - -typedef struct -{ - unsigned char namesz[4]; - unsigned char descsz[4]; - unsigned char type[4]; - unsigned char name[1]; -} Elf_External_Note; - -/* Information about type of the file. */ - -enum type_of_file -{ - FILE_TYPE_INVALID = -1, - FILE_TYPE_LINK = 1, - FILE_TYPE_REGULAR = 2 -}; - -/* Information about debug paths. */ - -enum debug_path -{ - DEBUG_PATH_CURRENT, - DEBUG_PATH_CURRENT_DEBUG, - DEBUG_PATH_USR_LIB_DEBUG, - DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE, - DEBUG_PATH_MAX -}; - -/* Type of the ELF file. */ - -enum type_of_elf -{ - ELF_TYPE_DYN = -1, - ELF_TYPE_INVALID = 0, - ELF_TYPE_EXEC = 1 -}; - -/* Paths to debug file. */ - -static const char *const debug_file_path[DEBUG_PATH_MAX] - = {"", ".debug/", "/usr/lib/debug/", "/usr/lib/debug"}; - -/* Get a uint32 from the buffer. */ - -static uint32_t -get_uint32 (const unsigned char *p, int is_bigendian) -{ - if (is_bigendian) - return (((uint32_t) p[0] << 24) | ((uint32_t) p[1] << 16) - | ((uint32_t) p[2] << 8) | (uint32_t) p[3]); - else - return (((uint32_t) p[3] << 24) | ((uint32_t) p[2] << 16) - | ((uint32_t) p[1] << 8) | (uint32_t) p[0]); -} - -/* Generate crc32 sum from the file. */ - -static uint32_t -get_crc32 (struct backtrace_state *state, int descriptor, - backtrace_error_callback error_callback, void *data) -{ - uint32_t file_crc; - off_t offset; - size_t buffer_count; - size_t buffer_len; - size_t pass; - size_t out_of_buffer_len; - const unsigned char *buffer; - struct stat file_stat; - struct backtrace_view file_view; - - buffer_len = 8 * 1024; - offset = 0; - file_crc = 0; - out_of_buffer_len = 0; - - memset (&file_stat, 0, sizeof (struct stat)); - - if (fstat (descriptor, &file_stat) == -1) - { - if (errno != ENOENT) - error_callback (data, "fstat", errno); - return 0; - } - - if (!backtrace_get_view (state, descriptor, offset, file_stat.st_size, - error_callback, data, &file_view)) - return 0; - - buffer = (const unsigned char *) file_view.data; - buffer_count = file_stat.st_size / buffer_len; - out_of_buffer_len = file_stat.st_size % buffer_len; - - for (pass = 0; pass < buffer_count; ++pass) - { - file_crc = gnu_debuglink_crc32 (file_crc, buffer + offset, buffer_len); - offset += buffer_len; - } - - if (out_of_buffer_len) - file_crc - = gnu_debuglink_crc32 (file_crc, buffer + offset, out_of_buffer_len); - - backtrace_release_view (state, &file_view, error_callback, data); - return file_crc; -} - -/* Get length of the base name. */ - -static size_t -base_name_len (const char *buffer) -{ - size_t len; - - len = strlen (buffer); - while (len > 1 && !IS_DIR_SEPARATOR (buffer[len - 1])) - --len; - return len > 1 ? len : 0; -} - -/* Verify crc32 sum. */ - -static unsigned int -check_sum (struct backtrace_state *state, int descriptor, - backtrace_error_callback error_callback, void *data, - const unsigned char *debug_link, size_t offset, size_t section_size, - int is_bigendian) -{ - uint32_t crc32_debug_link; - uint32_t crc32_debug_file; - offset += 1; - offset = (offset + 3) & ~3; - if (offset + 4 > section_size) - return 0; - crc32_debug_link = get_uint32 (debug_link + offset, is_bigendian); - crc32_debug_file = get_crc32 (state, descriptor, error_callback, data); - return crc32_debug_link == crc32_debug_file; -} - -/* Process elf header. Verify magic number, version, etc, of the ELF - header. Populate ehdr_out, names_view_out and shdrs_view_out. Caller - should release view of the names_view_out and shdrs_view_out. Return - ELF_TYPE_EXEC if the file type is EXE, ELF_TYPE_DYN if type of the - file is DYN and ELF_TYPE_INVALID on the fail. */ - -static enum type_of_elf -process_elf_header (struct backtrace_state *state, int descriptor, - backtrace_error_callback error_callback, void *data, - int exe, b_elf_ehdr *ehdr_out, - struct backtrace_view *shdrs_view_out, - struct backtrace_view *names_view_out) -{ - struct backtrace_view ehdr_view; - const b_elf_shdr *shstrhdr; - const b_elf_shdr *shdrs; - unsigned int shstrndx; - unsigned int shnum; - off_t shoff; - int shdrs_view_valid; - - shdrs_view_valid = 0; - - if (!backtrace_get_view (state, descriptor, 0, sizeof *ehdr_out, - error_callback, data, &ehdr_view)) - goto fail; - - memcpy (ehdr_out, ehdr_view.data, sizeof *ehdr_out); - - backtrace_release_view (state, &ehdr_view, error_callback, data); - - if (ehdr_out->e_ident[EI_MAG0] != ELFMAG0 - || ehdr_out->e_ident[EI_MAG1] != ELFMAG1 - || ehdr_out->e_ident[EI_MAG2] != ELFMAG2 - || ehdr_out->e_ident[EI_MAG3] != ELFMAG3) - { - error_callback (data, "processed file is not ELF", 0); - goto fail; - } - if (ehdr_out->e_ident[EI_VERSION] != EV_CURRENT) - { - error_callback (data, "processed file is unrecognized ELF version", 0); - goto fail; - } - -#if BACKTRACE_ELF_SIZE == 32 -#define BACKTRACE_ELFCLASS ELFCLASS32 -#else -#define BACKTRACE_ELFCLASS ELFCLASS64 -#endif - - if (ehdr_out->e_ident[EI_CLASS] != BACKTRACE_ELFCLASS) - { - error_callback (data, "processed file is unexpected ELF class", 0); - goto fail; - } - - if (ehdr_out->e_ident[EI_DATA] != ELFDATA2LSB - && ehdr_out->e_ident[EI_DATA] != ELFDATA2MSB) - { - error_callback (data, "processed file has unknown endianness", 0); - goto fail; - } - - /* If the executable is ET_DYN, it is either a PIE, or we are running - directly a shared library with .interp. We need to wait for - dl_iterate_phdr in that case to determine the actual base_address. */ - if (exe && ehdr_out->e_type == ET_DYN) - return ELF_TYPE_DYN; - - shoff = ehdr_out->e_shoff; - shnum = ehdr_out->e_shnum; - shstrndx = ehdr_out->e_shstrndx; - - if ((shnum == 0 || shstrndx == SHN_XINDEX) && shoff != 0) - { - struct backtrace_view shdr_view; - const b_elf_shdr *shdr; - - if (!backtrace_get_view (state, descriptor, shoff, sizeof (b_elf_shdr), - error_callback, data, &shdr_view)) - goto fail; - - shdr = (const b_elf_shdr *) shdr_view.data; - - if (shnum == 0) - shnum = shdr->sh_size; - - if (shstrndx == SHN_XINDEX) - { - shstrndx = shdr->sh_link; - - /* Versions of the GNU binutils between 2.12 and 2.18 did - not handle objects with more than SHN_LORESERVE sections - correctly. All large section indexes were offset by - 0x100. There is more information at - http://sourceware.org/bugzilla/show_bug.cgi?id-5900 . - Fortunately these object files are easy to detect, as the - GNU binutils always put the section header string table - near the end of the list of sections. Thus if the - section header string table index is larger than the - number of sections, then we know we have to subtract - 0x100 to get the real section index. */ - if (shstrndx >= shnum && shstrndx >= SHN_LORESERVE + 0x100) - shstrndx -= 0x100; - } - backtrace_release_view (state, &shdr_view, error_callback, data); - } - - /* Read the section headers, skipping the first one. */ - - if (!backtrace_get_view (state, descriptor, shoff + sizeof (b_elf_shdr), - (shnum - 1) * sizeof (b_elf_shdr), error_callback, - data, shdrs_view_out)) - goto fail; - - shdrs_view_valid = 1; - - shdrs = (const b_elf_shdr *) shdrs_view_out->data; - shstrhdr = &shdrs[shstrndx - 1]; - - /* Read the section names. */ - - if (!backtrace_get_view (state, descriptor, shstrhdr->sh_offset, - shstrhdr->sh_size, error_callback, data, - names_view_out)) - goto fail; - - ehdr_out->e_shoff = shoff; - ehdr_out->e_shnum = shnum; - ehdr_out->e_shstrndx = shstrndx; - - return ELF_TYPE_EXEC; - - fail: - /* In case fail happens with backtrace_get_view for names section. */ - if (shdrs_view_valid) - backtrace_release_view (state, shdrs_view_out, error_callback, data); - return ELF_TYPE_INVALID; -} - -/* Get content of the specifying section. */ - -static unsigned char * -elf_get_section_by_name (struct backtrace_state *state, int descriptor, - backtrace_error_callback error_callback, void *data, - size_t *section_data_len_out, const char *section_name, - b_elf_ehdr *ehdr, struct backtrace_view *shdrs_view, - struct backtrace_view *names_view) -{ - const b_elf_shdr *shdrs; - const b_elf_shdr *shstrhdr; - size_t shstr_size; - struct backtrace_view section_view; - const char *names; - unsigned int i; - unsigned int shnum; - unsigned int shstrndx; - int section_view_valid; - unsigned char *section_data; - - section_view_valid = 0; - section_data = NULL; - - shnum = ehdr->e_shnum; - shstrndx = ehdr->e_shstrndx; - shdrs = (const b_elf_shdr *) shdrs_view->data; - shstrhdr = &shdrs[shstrndx - 1]; - shstr_size = shstrhdr->sh_size; - - names = (const char *) names_view->data; - - for (i = 1; i < shnum; ++i) - { - const b_elf_shdr *shdr; - unsigned int sh_name; - const char *name; - shdr = &shdrs[i - 1]; - sh_name = shdr->sh_name; - if (sh_name >= shstr_size) - { - error_callback (data, "ELF section name out of range", 0); - goto exit; - } - - name = names + sh_name; - - if (strcmp (name, section_name) == 0) - { - if (backtrace_get_view (state, descriptor, shdr->sh_offset, - shdr->sh_size, error_callback, data, - §ion_view)) - { - section_view_valid = 1; - section_data - = backtrace_alloc (state, shdr->sh_size, error_callback, data); - if (section_data == NULL) - goto exit; - memcpy (section_data, section_view.data, shdr->sh_size); - *section_data_len_out = shdr->sh_size; - } - break; - } - } - - exit: - if (section_view_valid) - backtrace_release_view (state, §ion_view, error_callback, data); - return section_data; -} - -/* Verify type of the file. */ - -static enum type_of_file -backtrace_readlink (const char *filename, - backtrace_error_callback error_callback, void *data) -{ - struct stat link_stat; - enum type_of_file file_type; - mode_t mode; - - memset (&link_stat, 0, sizeof (struct stat)); - - if (lstat (filename, &link_stat) == -1) - { - if (errno != ENOENT) - error_callback (data, filename, errno); - file_type = FILE_TYPE_INVALID; - } - - mode = link_stat.st_mode & S_IFMT; - - switch (mode) - { - case S_IFLNK: - file_type = FILE_TYPE_LINK; - break; - case S_IFREG: - file_type = FILE_TYPE_REGULAR; - break; - default: - file_type = FILE_TYPE_INVALID; - } - return file_type; -} - -/* Resolve full name of the link. In this case we can't use realpath function - because it could be undefined on some platfroms, also it allocates memory - by malloc, which we can't use. */ - -static ssize_t -resolve_realname (const char *filename, char *buffer, - struct backtrace_state *state, - backtrace_error_callback error_callback, void *data) -{ - char *temp_buffer; - enum type_of_file file_type; - ssize_t filename_len; - size_t temp_filename_len; - size_t base_len; - int valid_temp_buffer; - - valid_temp_buffer = 0; - filename_len = -1; - file_type = FILE_TYPE_LINK; - - /* Allocate memory for sizeof(PATH_MAX) + 1 bytes because at this time - we don't know how long path could be. */ - temp_buffer = backtrace_alloc (state, PATH_MAX + 1, error_callback, data); - if (temp_buffer == NULL) - return -1; - - valid_temp_buffer = 1; - - memset (temp_buffer, 0, PATH_MAX + 1); - memcpy (temp_buffer, filename, strlen (filename)); - - while (file_type == FILE_TYPE_LINK) - { - filename_len = readlink (temp_buffer, buffer, PATH_MAX); - if (filename_len < 1) - goto exit; - - temp_filename_len = strlen (buffer); - - /* Full path. */ - if (IS_ABSOLUTE_PATH (buffer)) - { - memset (temp_buffer, 0, PATH_MAX); - memcpy (temp_buffer, buffer, temp_filename_len); - } - else - { - /* Relative path. */ - base_len = base_name_len (temp_buffer); - if (!base_len || (base_len + filename_len > PATH_MAX)) - { - filename_len = -1; - goto exit; - } - memcpy (temp_buffer + base_len, buffer, filename_len); - temp_buffer[base_len + filename_len] = '\0'; - } - - file_type = backtrace_readlink (temp_buffer, error_callback, data); - memset (buffer, 0, filename_len); - } - - if (file_type != FILE_TYPE_REGULAR) - { - filename_len = -1; - goto exit; - } - - filename_len = strlen (temp_buffer); - memcpy (buffer, temp_buffer, filename_len); - - exit: - if (valid_temp_buffer) - backtrace_free (state, temp_buffer, PATH_MAX + 1, error_callback, data); - return filename_len; -} - -/* Resolve realname of the filename. This function verifies filename. - If filename is name of the file it populates realname buffer. - If filename is link, it calls resolve_realname function. */ - -static unsigned int -backtrace_resolve_realname (const char *filename, char *realname, - struct backtrace_state *state, - backtrace_error_callback error_callback, void *data) -{ - enum type_of_file file_type; - ssize_t filename_len; - - filename_len = -1; - - file_type = backtrace_readlink (filename, error_callback, data); - - if (file_type == FILE_TYPE_LINK) - { - /* Read the actual filename. */ - filename_len - = resolve_realname (filename, realname, state, error_callback, data); - if (filename_len < 1) - return 0; - } - else if (file_type == FILE_TYPE_REGULAR) - { - filename_len = strlen (filename); - if (filename_len > PATH_MAX) - return 0; - memcpy (realname, filename, filename_len); - } - /* FILE_TYPE_INVALID. */ - else - return 0; - - return 1; -} - -/* Search for debug file into specifying directorires. */ - -static int -search_for_debugfile (char *realname, char *debug_filename, - backtrace_error_callback error_callback, void *data, - struct backtrace_state *state) -{ - size_t debug_filename_len; - size_t pass; - size_t debug_path_len; - size_t buffer_len; - size_t base_len; - int debug_does_not_exist; - int debug_descriptor; - int valid_buffer; - char *buffer; - - debug_descriptor = -1; - valid_buffer = 0; - - base_len = base_name_len (realname); - - if (!base_len) - return debug_descriptor; - - debug_filename_len = strlen ((const char *) debug_filename); - - if (!debug_filename_len) - return debug_descriptor; - - buffer_len = base_len + strlen (debug_file_path[DEBUG_PATH_USR_LIB_DEBUG]) - + debug_filename_len + 1; - - buffer = backtrace_alloc (state, buffer_len, error_callback, data); - - if (buffer == NULL) - return debug_descriptor; - - valid_buffer = 1; - - memset (buffer, 0, buffer_len); - memcpy (buffer, realname, base_len); - - for (pass = 0; pass < DEBUG_PATH_MAX; ++pass) - { - switch (pass) - { - case DEBUG_PATH_CURRENT: - { - memcpy (buffer + base_len, debug_filename, debug_filename_len); - break; - } - case DEBUG_PATH_CURRENT_DEBUG: - { - debug_path_len = strlen (debug_file_path[DEBUG_PATH_CURRENT_DEBUG]); - memcpy (buffer + base_len, - debug_file_path[DEBUG_PATH_CURRENT_DEBUG], debug_path_len); - memcpy (buffer + base_len + debug_path_len, debug_filename, - debug_filename_len); - break; - } - case DEBUG_PATH_USR_LIB_DEBUG: - { - debug_path_len = strlen (debug_file_path[DEBUG_PATH_USR_LIB_DEBUG]); - memset (buffer, 0, buffer_len); - memcpy (buffer, debug_file_path[DEBUG_PATH_USR_LIB_DEBUG], - debug_path_len); - memcpy (buffer + debug_path_len, debug_filename, - debug_filename_len); - break; - } - case DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE: - { - debug_path_len - = strlen (debug_file_path[DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE]); - memset (buffer, 0, buffer_len); - memcpy (buffer, - debug_file_path[DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE], - debug_path_len); - memcpy (buffer + debug_path_len, realname, base_len); - memcpy (buffer + debug_path_len + base_len, debug_filename, - debug_filename_len); - break; - } - default: - goto exit; - } - - debug_descriptor - = backtrace_open (buffer, error_callback, data, &debug_does_not_exist); - - if (debug_descriptor >= 0) - break; - } - - exit: - if (valid_buffer) - backtrace_free (state, buffer, buffer_len, error_callback, data); - return debug_descriptor; -} - -/* Open debug file by gnulink. */ - -static int -open_debugfile_by_gnulink (char *realname, unsigned char *section_data, - size_t section_data_len, int is_bigendian, - struct backtrace_state *state, - backtrace_error_callback error_callback, void *data) -{ - int debug_descriptor; - - debug_descriptor = search_for_debugfile (realname, (char *) section_data, - error_callback, data, state); - if (debug_descriptor < 0) - return -1; - - /* Check the crc32 checksum if it is not the same return -1. */ - - if (!check_sum (state, debug_descriptor, error_callback, data, - (const unsigned char *) section_data, - strlen ((char *) section_data), section_data_len, - is_bigendian)) - { - /* If crc32 sums are different, just close the descriptor - associated with debuginfo file. */ - backtrace_close (debug_descriptor, error_callback, data); - debug_descriptor = -1; - } - - return debug_descriptor; -} - -/* Convert char to hex */ - -static char -hex (char ch) -{ - return ch > 9 ? ('a' + (ch - 10)) : ('0' + ch); -} - -/* Get build-id name. */ - -static char * -get_build_id_name (unsigned char *section_data, size_t *len, - int is_bigendian, struct backtrace_state *state, - backtrace_error_callback error_callback, void *data) -{ - Elf_External_Note *build_id_section; - char *build_id_name; - char *temp; - const char *debug_postfix; - const char *debug_prefix; - size_t debug_postfix_len; - size_t debug_prefix_len; - size_t name_size; - size_t offset; - unsigned char *hash_start; - uint32_t hash_size; - uint32_t identifier; - - debug_postfix_len = 6; - debug_prefix_len = 10; - debug_postfix = ".debug"; - debug_prefix = ".build-id/"; - - build_id_section = (Elf_External_Note *) section_data; - hash_size = get_uint32 (build_id_section->descsz, is_bigendian); - identifier = get_uint32 (build_id_section->type, is_bigendian); - name_size = get_uint32 (build_id_section->namesz, is_bigendian); - - if (identifier != NT_GNU_BUILD_ID || hash_size == 0 || name_size != 4 - || strncmp ((char *) build_id_section->name, "GNU", 3) != 0) - return NULL; - - offset = 16; - hash_start = section_data + offset; - *len = hash_size * 2 + debug_postfix_len + debug_prefix_len + 1; - build_id_name = backtrace_alloc (state, *len, error_callback, data); - - if (build_id_name == NULL) - { - *len = 0; - return NULL; - } - - memset (build_id_name, 0, *len); - memcpy (build_id_name, debug_prefix, debug_prefix_len); - temp = build_id_name + debug_prefix_len; - - *temp++ = hex ((*hash_start & 0xF0) >> 4); - *temp++ = hex (*hash_start & 0x0F); - ++hash_start; - --hash_size; - *temp++ = DIR_SEPARATOR; - - while (hash_size--) - { - *temp++ = hex ((*hash_start & 0xF0) >> 4); - *temp++ = hex (*hash_start & 0x0F); - ++hash_start; - } - - memcpy (temp, debug_postfix, debug_postfix_len); - return build_id_name; -} - -/* Open file by build-id. */ - -static int -open_debugfile_by_build_id (char *realname, unsigned char *section_data, - int is_bigendian, struct backtrace_state *state, - backtrace_error_callback error_callback, void *data) - -{ - char *build_id_name; - int debug_descriptor; - size_t build_id_name_len; - int valid_build_id_name; - - debug_descriptor = -1; - valid_build_id_name = 0; - build_id_name_len = 0; - - build_id_name = get_build_id_name (section_data, &build_id_name_len, - is_bigendian, state, error_callback, data); - - if (!build_id_name_len) - return -1; - - valid_build_id_name = 1; - - debug_descriptor = search_for_debugfile (realname, build_id_name, - error_callback, data, state); - - if (valid_build_id_name) - backtrace_free (state, build_id_name, build_id_name_len, error_callback, - data); - return debug_descriptor; -} - -/* Open debug file. */ - -static int -backtrace_open_debugfile (int descriptor, const char *filename, - backtrace_error_callback error_callback, void *data, - struct backtrace_state *state) -{ - int debug_descriptor; - unsigned char *gnulink_section_data; - unsigned char *build_id_section_data; - int valid_descriptor; - int valid_gnulink_section_data; - int valid_build_id_section_data; - int valid_realname; - int valid_elf_header; - size_t build_id_section_data_len; - size_t gnu_link_section_data_len; - char *realname; - b_elf_ehdr ehdr; - struct backtrace_view shdrs_view; - struct backtrace_view names_view; - - valid_elf_header = 0; - valid_realname = 0; - valid_descriptor = 0; - valid_gnulink_section_data = 0; - valid_build_id_section_data = 0; - build_id_section_data_len = 0; - gnu_link_section_data_len = 0; - debug_descriptor = -1; - - if (!process_elf_header (state, descriptor, error_callback, data, 0, &ehdr, - &shdrs_view, &names_view)) - return -1; - - valid_elf_header = 1; - - realname = backtrace_alloc (state, PATH_MAX + 1, error_callback, data); - - if (realname == NULL) - goto exit; - - /* Indicates that we successfully allocated memory. */ - valid_realname = 1; - - /* Populate the buffer with realname. */ - if (!backtrace_resolve_realname (filename, realname, state, error_callback, - data)) - goto exit; - - /* Check if build-id section does exist. */ - build_id_section_data - = elf_get_section_by_name (state, descriptor, error_callback, data, - &build_id_section_data_len, ".note.gnu.build-id", - &ehdr, &shdrs_view, &names_view); - - if (build_id_section_data_len) - { - valid_build_id_section_data = 1; - debug_descriptor - = open_debugfile_by_build_id (realname, build_id_section_data, - ehdr.e_ident[EI_DATA] == ELFDATA2MSB, - state, error_callback, data); - } - - if (debug_descriptor < 0) - { - gnulink_section_data - = elf_get_section_by_name (state, descriptor, error_callback, data, - &gnu_link_section_data_len, ".gnu_debuglink", - &ehdr, &shdrs_view, &names_view); - - if (gnu_link_section_data_len) - { - valid_gnulink_section_data = 1; - debug_descriptor - = open_debugfile_by_gnulink (realname, gnulink_section_data, - gnu_link_section_data_len, - ehdr.e_ident[EI_DATA] == ELFDATA2MSB, - state, error_callback, data); - } - } - - if (debug_descriptor >= 0) - valid_descriptor = 1; - - exit: - if (valid_descriptor) - backtrace_close (descriptor, error_callback, data); - if (valid_gnulink_section_data) - backtrace_free (state, gnulink_section_data, gnu_link_section_data_len, - error_callback, data); - if (valid_build_id_section_data) - backtrace_free (state, build_id_section_data, build_id_section_data_len, - error_callback, data); - if (valid_realname) - backtrace_free (state, realname, PATH_MAX + 1, error_callback, data); - if (valid_elf_header) - { - backtrace_release_view (state, &names_view, error_callback, data); - backtrace_release_view (state, &shdrs_view, error_callback, data); - } - return debug_descriptor; -} - /* A dummy callback function used when we can't find any debug info. */ static int @@ -1389,7 +521,9 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, backtrace_error_callback error_callback, void *data, fileline *fileline_fn, int *found_sym, int *found_dwarf, int exe) { + struct backtrace_view ehdr_view; b_elf_ehdr ehdr; + off_t shoff; unsigned int shnum; unsigned int shstrndx; struct backtrace_view shdrs_view; @@ -1397,6 +531,7 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, const b_elf_shdr *shdrs; const b_elf_shdr *shstrhdr; size_t shstr_size; + off_t shstr_off; struct backtrace_view names_view; int names_view_valid; const char *names; @@ -1412,7 +547,6 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, off_t max_offset; struct backtrace_view debug_view; int debug_view_valid; - enum type_of_elf elf_type; *found_sym = 0; *found_dwarf = 0; @@ -1423,32 +557,116 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, strtab_view_valid = 0; debug_view_valid = 0; - elf_type = process_elf_header (state, descriptor, error_callback, data, exe, - &ehdr, &shdrs_view, &names_view); - switch (elf_type) + if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback, + data, &ehdr_view)) + goto fail; + + memcpy (&ehdr, ehdr_view.data, sizeof ehdr); + + backtrace_release_view (state, &ehdr_view, error_callback, data); + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3) + { + error_callback (data, "executable file is not ELF", 0); + goto fail; + } + if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { - /* Binary compiled with PIE option. */ - case ELF_TYPE_DYN: - return -1; - case ELF_TYPE_EXEC: - break; - /* Header is invalid. */ - case ELF_TYPE_INVALID: + error_callback (data, "executable file is unrecognized ELF version", 0); goto fail; } - shdrs_view_valid = 1; - names_view_valid = 1; +#if BACKTRACE_ELF_SIZE == 32 +#define BACKTRACE_ELFCLASS ELFCLASS32 +#else +#define BACKTRACE_ELFCLASS ELFCLASS64 +#endif + + if (ehdr.e_ident[EI_CLASS] != BACKTRACE_ELFCLASS) + { + error_callback (data, "executable file is unexpected ELF class", 0); + goto fail; + } + + if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB + && ehdr.e_ident[EI_DATA] != ELFDATA2MSB) + { + error_callback (data, "executable file has unknown endianness", 0); + goto fail; + } + + /* If the executable is ET_DYN, it is either a PIE, or we are running + directly a shared library with .interp. We need to wait for + dl_iterate_phdr in that case to determine the actual base_address. */ + if (exe && ehdr.e_type == ET_DYN) + return -1; + + shoff = ehdr.e_shoff; + shnum = ehdr.e_shnum; + shstrndx = ehdr.e_shstrndx; + + if ((shnum == 0 || shstrndx == SHN_XINDEX) + && shoff != 0) + { + struct backtrace_view shdr_view; + const b_elf_shdr *shdr; + + if (!backtrace_get_view (state, descriptor, shoff, sizeof shdr, + error_callback, data, &shdr_view)) + goto fail; + + shdr = (const b_elf_shdr *) shdr_view.data; + + if (shnum == 0) + shnum = shdr->sh_size; + + if (shstrndx == SHN_XINDEX) + { + shstrndx = shdr->sh_link; + + /* Versions of the GNU binutils between 2.12 and 2.18 did + not handle objects with more than SHN_LORESERVE sections + correctly. All large section indexes were offset by + 0x100. There is more information at + http://sourceware.org/bugzilla/show_bug.cgi?id-5900 . + Fortunately these object files are easy to detect, as the + GNU binutils always put the section header string table + near the end of the list of sections. Thus if the + section header string table index is larger than the + number of sections, then we know we have to subtract + 0x100 to get the real section index. */ + if (shstrndx >= shnum && shstrndx >= SHN_LORESERVE + 0x100) + shstrndx -= 0x100; + } + + backtrace_release_view (state, &shdr_view, error_callback, data); + } /* To translate PC to file/line when using DWARF, we need to find the .debug_info and .debug_line sections. */ + /* Read the section headers, skipping the first one. */ + + if (!backtrace_get_view (state, descriptor, shoff + sizeof (b_elf_shdr), + (shnum - 1) * sizeof (b_elf_shdr), + error_callback, data, &shdrs_view)) + goto fail; + shdrs_view_valid = 1; shdrs = (const b_elf_shdr *) shdrs_view.data; - shnum = ehdr.e_shnum; - shstrndx = ehdr.e_shstrndx; + /* Read the section names. */ + shstrhdr = &shdrs[shstrndx - 1]; shstr_size = shstrhdr->sh_size; + shstr_off = shstrhdr->sh_offset; + + if (!backtrace_get_view (state, descriptor, shstr_off, shstr_size, + error_callback, data, &names_view)) + goto fail; + names_view_valid = 1; names = (const char *) names_view.data; symtab_shndx = 0; @@ -1659,7 +877,6 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, int does_not_exist; fileline elf_fileline_fn; int found_dwarf; - int debug_descriptor; /* There is not much we can do if we don't have the module name, unless executable is ET_DYN, where we expect the very first @@ -1678,19 +895,11 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, backtrace_close (pd->exe_descriptor, pd->error_callback, pd->data); pd->exe_descriptor = -1; } - debug_descriptor = -1; descriptor = backtrace_open (info->dlpi_name, pd->error_callback, pd->data, &does_not_exist); - if (descriptor < 0) return 0; - - debug_descriptor - = backtrace_open_debugfile (descriptor, info->dlpi_name, - pd->error_callback, pd->data, pd->state); - if (debug_descriptor >= 0) - descriptor = debug_descriptor; } if (elf_add (pd->state, descriptor, info->dlpi_addr, pd->error_callback, @@ -1706,53 +915,6 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, return 0; } -/* On succcess returns pathname of the executable. On fail returns NULL. */ -static const char * -get_exec_filename (backtrace_error_callback error_callback, void *data) -{ - int descriptor; - const char *filename; - unsigned int pass; - - descriptor = -1; - filename = NULL; - - for (pass = 0; pass < 4; ++pass) - { - int does_not_exist; - - switch (pass) - { - case 0: - filename = getexecname (); - break; - case 1: - filename = "/proc/self/exe"; - break; - case 2: - filename = "/proc/curproc/file"; - break; - default: - filename = NULL; - break; - } - - if (filename == NULL) - continue; - - descriptor - = backtrace_open (filename, error_callback, data, &does_not_exist); - - if (descriptor >= 0) - break; - } - - if (descriptor >= 0) - backtrace_close (descriptor, error_callback, data); - - return filename; -} - /* Initialize the backtrace data we need from an ELF executable. At the ELF level, all we need to do is find the debug info sections. */ @@ -1767,24 +929,6 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, int found_dwarf; fileline elf_fileline_fn = elf_nodebug; struct phdr_data pd; - int debug_descriptor; - const char *filename; - - debug_descriptor = -1; - - /* In case state->filename is not initialized, we should resolve the - executable name, because that name is needed to find the path to - the file with debug info. So to avoid changing API for - backtrace_initalize we can do it with some overhead by iterating - through all possible names and trying to open the file. */ - filename = state->filename == NULL ? get_exec_filename (error_callback, data) - : state->filename; - - if (filename != NULL) - debug_descriptor = backtrace_open_debugfile (descriptor, filename, - error_callback, data, state); - if (debug_descriptor >= 0) - descriptor = debug_descriptor; ret = elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn, &found_sym, &found_dwarf, 1); -- 2.7.4 From 36676e9f8ff8a4e34dda6ea05eae81dd6357f46b Mon Sep 17 00:00:00 2001 From: Denis Khalikov Date: Thu, 7 Jun 2018 13:20:04 +0300 Subject: [PATCH 04/16] libbacktrace: backport from mainline. * btest.c (test5): Replace #ifdef guard with 'unused' attribute to fix compile warning when BACKTRACE_SUPPORTED isn't defined. * dwarf.c (free_line_header): Don't free dirs if dirs_count == 0. (read_line_header): Don't allocate dirs if dirs_count == 0. * edtest.c: New file. * edtest2.c: New file. * Makefile.am (edtest_SOURCES, edtest_LDADD): Define. (check_PROGRAMS): Add edtest. (edtest2_build.c, gen_edtest2_build): New targets. * Makefile.in: Rebuild. * elf.c (backtrace_initialize): Always set *fileline_fn. * ttest.c: New file. * btest.c: Move support functions into testlib.c. Change calls to check to pass file name. * testlib.c: New file, copied from (part of) btest.c. * testlib.h: New file, declarations for testlib.c. * edtest.c: Use testlib.h and testlib.c. * configure.ac: Test for -pthread, set HAVE_PTHREAD conditional. * Makefile.am (btest_SOURCES): Add testlib.c. (edtest_SOURCES): Likewise. (CHECK_PROGRAMS): Add ttest if HAVE_PTHREAD. (ttest_SOURCES, ttest_CFLAGS, ttest_LDADD): Define. * configure, Makefile.in: Rebuild. * configure.ac: Add AC_SYS_LARGEFILE. * config.h.in: Regenerate. * configure: Likewise. * filetype.awk: Add AIX XCOFF type detection. * configure.ac: Recognize xcoff format. * Makefile.am (FORMAT_FILES): Add xcoff.c. * fileline.c: Include . (fileline_initialize): Add case for AIX procfs. * xcoff.c: New file. * configure, Makefile.in: Rebuild. * configure.ac: Check for XCOFF32/XCOFF64. Check for loadquery. * filetype.awk: Separate AIX XCOFF32 and XCOFF64. * xcoff.c: Add support for AIX XCOFF32 and XCOFF64 formats. * configure, config.h.in: Regenerate. * fileline.c (fileline_initialize): Print pid_t as long. * xcoff.c: Don't leak a file descriptor if an archive is malformed. * xcoff.c (xcoff_process_linenos): Initialize incl to NULL. * libbacktrace/Makefile.in (HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ttest_CFLAGS): Add $(AM_CFLAGS) * Makefile.am (ttest_CFLAGS): Add $(AM_CFLAGS) * Makefile.in: Regenerate. Change-Id: Ib61ad222b79b9c3a38d35705608f74041acc767a --- libbacktrace/ChangeLog | 80 +++ libbacktrace/Makefile.am | 30 +- libbacktrace/Makefile.in | 69 ++- libbacktrace/btest.c | 271 +-------- libbacktrace/config.h.in | 15 + libbacktrace/configure | 311 +++++++++- libbacktrace/configure.ac | 49 ++ libbacktrace/dwarf.c | 25 +- libbacktrace/edtest.c | 121 ++++ libbacktrace/edtest2.c | 43 ++ libbacktrace/elf.c | 16 +- libbacktrace/fileline.c | 9 +- libbacktrace/filetype.awk | 3 + libbacktrace/testlib.c | 234 +++++++ libbacktrace/testlib.h | 110 ++++ libbacktrace/ttest.c | 161 +++++ libbacktrace/xcoff.c | 1485 +++++++++++++++++++++++++++++++++++++++++++++ 17 files changed, 2740 insertions(+), 292 deletions(-) create mode 100644 libbacktrace/edtest.c create mode 100644 libbacktrace/edtest2.c create mode 100644 libbacktrace/testlib.c create mode 100644 libbacktrace/testlib.h create mode 100644 libbacktrace/ttest.c create mode 100644 libbacktrace/xcoff.c diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog index 4cb639b..e2285fd 100644 --- a/libbacktrace/ChangeLog +++ b/libbacktrace/ChangeLog @@ -1,3 +1,83 @@ +2017-09-12 Steve Ellcey + + PR other/81096 + * Makefile.am (ttest_CFLAGS): Add $(AM_CFLAGS) + * Makefile.in: Regenerate. + +2017-09-12 Steve Ellcey + + PR other/81096 + * libbacktrace/Makefile.in + (HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ttest_CFLAGS): Add $(AM_CFLAGS) + +2017-08-02 David Edelsohn + + PR bootstrap/81638 + * xcoff.c (xcoff_process_linenos): Initialize incl to NULL. + +2017-07-28 Tony Reix + + * xcoff.c: Don't leak a file descriptor if an archive is malformed. + +2017-07-28 Rainer Orth + + * fileline.c (fileline_initialize): Print pid_t as long. + +2017-07-26 Tony Reix + + * configure.ac: Check for XCOFF32/XCOFF64. Check for loadquery. + * filetype.awk: Separate AIX XCOFF32 and XCOFF64. + * xcoff.c: Add support for AIX XCOFF32 and XCOFF64 formats. + * configure, config.h.in: Regenerate. + +2017-07-21 Tony Reix + + * filetype.awk: Add AIX XCOFF type detection. + * configure.ac: Recognize xcoff format. + * Makefile.am (FORMAT_FILES): Add xcoff.c. + * fileline.c: Include . + (fileline_initialize): Add case for AIX procfs. + * xcoff.c: New file. + * configure, Makefile.in: Rebuild. + +2017-06-21 Richard Biener + + * configure.ac: Add AC_SYS_LARGEFILE. + * config.h.in: Regenerate. + * configure: Likewise. + +2017-06-11 Ian Lance Taylor + + * elf.c (backtrace_initialize): Always set *fileline_fn. + * ttest.c: New file. + * btest.c: Move support functions into testlib.c. Change calls to + check to pass file name. + * testlib.c: New file, copied from (part of) btest.c. + * testlib.h: New file, declarations for testlib.c. + * edtest.c: Use testlib.h and testlib.c. + * configure.ac: Test for -pthread, set HAVE_PTHREAD conditional. + * Makefile.am (btest_SOURCES): Add testlib.c. + (edtest_SOURCES): Likewise. + (CHECK_PROGRAMS): Add ttest if HAVE_PTHREAD. + (ttest_SOURCES, ttest_CFLAGS, ttest_LDADD): Define. + * configure, Makefile.in: Rebuild. + +2017-05-19 Than McIntosh + + * dwarf.c (free_line_header): Don't free dirs if dirs_count == 0. + (read_line_header): Don't allocate dirs if dirs_count == 0. + * edtest.c: New file. + * edtest2.c: New file. + * Makefile.am (edtest_SOURCES, edtest_LDADD): Define. + (check_PROGRAMS): Add edtest. + (edtest2_build.c, gen_edtest2_build): New targets. + * Makefile.in: Rebuild. + +2017-03-08 Sam Thursfield + + * btest.c (test5): Replace #ifdef guard with 'unused' attribute + to fix compile warning when BACKTRACE_SUPPORTED isn't defined. + 2016-12-21 Release Manager * GCC 6.3.0 released. diff --git a/libbacktrace/Makefile.am b/libbacktrace/Makefile.am index a7df025..ec7dde0 100644 --- a/libbacktrace/Makefile.am +++ b/libbacktrace/Makefile.am @@ -1,5 +1,5 @@ # Makefile.am -- Backtrace Makefile. -# Copyright (C) 2012-2016 Free Software Foundation, Inc. +# Copyright (C) 2012-2017 Free Software Foundation, Inc. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -57,7 +57,8 @@ BACKTRACE_FILES = \ FORMAT_FILES = \ elf.c \ pecoff.c \ - unknown.c + unknown.c \ + xcoff.c VIEW_FILES = \ read.c \ @@ -89,7 +90,7 @@ TESTS = $(check_PROGRAMS) if NATIVE -btest_SOURCES = btest.c +btest_SOURCES = btest.c testlib.c btest_CFLAGS = $(AM_CFLAGS) -g -O btest_LDADD = libbacktrace.la @@ -100,6 +101,27 @@ stest_LDADD = libbacktrace.la check_PROGRAMS += stest +edtest_SOURCES = edtest.c edtest2_build.c testlib.c +edtest_LDADD = libbacktrace.la + +check_PROGRAMS += edtest + +edtest2_build.c: gen_edtest2_build; @true +gen_edtest2_build: $(srcdir)/edtest2.c + cat $(srcdir)/edtest2.c > tmp-edtest2_build.c + $(SHELL) $(srcdir)/../move-if-change tmp-edtest2_build.c edtest2_build.c + echo timestamp > $@ + +if HAVE_PTHREAD + +check_PROGRAMS += ttest + +ttest_SOURCES = ttest.c testlib.c +ttest_CFLAGS = $(AM_CFLAGS) -pthread +ttest_LDADD = libbacktrace.la + +endif HAVE_PTHREAD + endif NATIVE # We can't use automake's automatic dependency tracking, because it @@ -134,3 +156,5 @@ sort.lo: config.h backtrace.h internal.h stest.lo: config.h backtrace.h internal.h state.lo: config.h backtrace.h backtrace-supported.h internal.h unknown.lo: config.h backtrace.h internal.h +xcoff.lo: config.h backtrace.h internal.h + diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in index 586b6a6..be5b4b6 100644 --- a/libbacktrace/Makefile.in +++ b/libbacktrace/Makefile.in @@ -83,8 +83,9 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ -check_PROGRAMS = $(am__EXEEXT_1) -@NATIVE_TRUE@am__append_1 = btest stest +check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) +@NATIVE_TRUE@am__append_1 = btest stest edtest +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__append_2 = ttest subdir = . DIST_COMMON = README ChangeLog $(srcdir)/Makefile.in \ $(srcdir)/Makefile.am $(top_srcdir)/configure \ @@ -113,16 +114,31 @@ am__DEPENDENCIES_1 = am_libbacktrace_la_OBJECTS = atomic.lo dwarf.lo fileline.lo posix.lo \ print.lo sort.lo state.lo libbacktrace_la_OBJECTS = $(am_libbacktrace_la_OBJECTS) -@NATIVE_TRUE@am__EXEEXT_1 = btest$(EXEEXT) stest$(EXEEXT) -@NATIVE_TRUE@am_btest_OBJECTS = btest-btest.$(OBJEXT) +@NATIVE_TRUE@am__EXEEXT_1 = btest$(EXEEXT) stest$(EXEEXT) \ +@NATIVE_TRUE@ edtest$(EXEEXT) +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__EXEEXT_2 = ttest$(EXEEXT) +@NATIVE_TRUE@am_btest_OBJECTS = btest-btest.$(OBJEXT) \ +@NATIVE_TRUE@ btest-testlib.$(OBJEXT) btest_OBJECTS = $(am_btest_OBJECTS) @NATIVE_TRUE@btest_DEPENDENCIES = libbacktrace.la btest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(btest_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ +@NATIVE_TRUE@am_edtest_OBJECTS = edtest.$(OBJEXT) \ +@NATIVE_TRUE@ edtest2_build.$(OBJEXT) testlib.$(OBJEXT) +edtest_OBJECTS = $(am_edtest_OBJECTS) +@NATIVE_TRUE@edtest_DEPENDENCIES = libbacktrace.la @NATIVE_TRUE@am_stest_OBJECTS = stest.$(OBJEXT) stest_OBJECTS = $(am_stest_OBJECTS) @NATIVE_TRUE@stest_DEPENDENCIES = libbacktrace.la +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am_ttest_OBJECTS = \ +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ ttest-ttest.$(OBJEXT) \ +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ ttest-testlib.$(OBJEXT) +ttest_OBJECTS = $(am_ttest_OBJECTS) +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ttest_DEPENDENCIES = libbacktrace.la +ttest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(ttest_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = am__depfiles_maybe = @@ -136,7 +152,8 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ SOURCES = $(libbacktrace_la_SOURCES) $(EXTRA_libbacktrace_la_SOURCES) \ - $(btest_SOURCES) $(stest_SOURCES) + $(btest_SOURCES) $(edtest_SOURCES) $(stest_SOURCES) \ + $(ttest_SOURCES) MULTISRCTOP = MULTIBUILDTOP = MULTIDIRS = @@ -213,6 +230,7 @@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIC_FLAG = @PIC_FLAG@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ @@ -301,7 +319,8 @@ BACKTRACE_FILES = \ FORMAT_FILES = \ elf.c \ pecoff.c \ - unknown.c + unknown.c \ + xcoff.c VIEW_FILES = \ read.c \ @@ -325,11 +344,16 @@ libbacktrace_la_LIBADD = \ libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD) TESTS = $(check_PROGRAMS) -@NATIVE_TRUE@btest_SOURCES = btest.c +@NATIVE_TRUE@btest_SOURCES = btest.c testlib.c @NATIVE_TRUE@btest_CFLAGS = $(AM_CFLAGS) -g -O @NATIVE_TRUE@btest_LDADD = libbacktrace.la @NATIVE_TRUE@stest_SOURCES = stest.c @NATIVE_TRUE@stest_LDADD = libbacktrace.la +@NATIVE_TRUE@edtest_SOURCES = edtest.c edtest2_build.c testlib.c +@NATIVE_TRUE@edtest_LDADD = libbacktrace.la +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ttest_SOURCES = ttest.c testlib.c +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ttest_CFLAGS = $(AM_CFLAGS) -pthread +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ttest_LDADD = libbacktrace.la # We can't use automake's automatic dependency tracking, because it # breaks when using bootstrap-lean. Automatic dependency tracking @@ -422,9 +446,15 @@ clean-checkPROGRAMS: btest$(EXEEXT): $(btest_OBJECTS) $(btest_DEPENDENCIES) $(EXTRA_btest_DEPENDENCIES) @rm -f btest$(EXEEXT) $(btest_LINK) $(btest_OBJECTS) $(btest_LDADD) $(LIBS) +edtest$(EXEEXT): $(edtest_OBJECTS) $(edtest_DEPENDENCIES) $(EXTRA_edtest_DEPENDENCIES) + @rm -f edtest$(EXEEXT) + $(LINK) $(edtest_OBJECTS) $(edtest_LDADD) $(LIBS) stest$(EXEEXT): $(stest_OBJECTS) $(stest_DEPENDENCIES) $(EXTRA_stest_DEPENDENCIES) @rm -f stest$(EXEEXT) $(LINK) $(stest_OBJECTS) $(stest_LDADD) $(LIBS) +ttest$(EXEEXT): $(ttest_OBJECTS) $(ttest_DEPENDENCIES) $(EXTRA_ttest_DEPENDENCIES) + @rm -f ttest$(EXEEXT) + $(ttest_LINK) $(ttest_OBJECTS) $(ttest_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -447,6 +477,24 @@ btest-btest.o: btest.c btest-btest.obj: btest.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(btest_CFLAGS) $(CFLAGS) -c -o btest-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi` +btest-testlib.o: testlib.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(btest_CFLAGS) $(CFLAGS) -c -o btest-testlib.o `test -f 'testlib.c' || echo '$(srcdir)/'`testlib.c + +btest-testlib.obj: testlib.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(btest_CFLAGS) $(CFLAGS) -c -o btest-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` + +ttest-ttest.o: ttest.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ttest_CFLAGS) $(CFLAGS) -c -o ttest-ttest.o `test -f 'ttest.c' || echo '$(srcdir)/'`ttest.c + +ttest-ttest.obj: ttest.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ttest_CFLAGS) $(CFLAGS) -c -o ttest-ttest.obj `if test -f 'ttest.c'; then $(CYGPATH_W) 'ttest.c'; else $(CYGPATH_W) '$(srcdir)/ttest.c'; fi` + +ttest-testlib.o: testlib.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ttest_CFLAGS) $(CFLAGS) -c -o ttest-testlib.o `test -f 'testlib.c' || echo '$(srcdir)/'`testlib.c + +ttest-testlib.obj: testlib.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ttest_CFLAGS) $(CFLAGS) -c -o ttest-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` + mostlyclean-libtool: -rm -f *.lo @@ -745,6 +793,12 @@ uninstall-am: mostlyclean-multi pdf pdf-am ps ps-am tags uninstall \ uninstall-am + +@NATIVE_TRUE@edtest2_build.c: gen_edtest2_build; @true +@NATIVE_TRUE@gen_edtest2_build: $(srcdir)/edtest2.c +@NATIVE_TRUE@ cat $(srcdir)/edtest2.c > tmp-edtest2_build.c +@NATIVE_TRUE@ $(SHELL) $(srcdir)/../move-if-change tmp-edtest2_build.c edtest2_build.c +@NATIVE_TRUE@ echo timestamp > $@ alloc.lo: config.h backtrace.h internal.h backtrace.lo: config.h backtrace.h internal.h btest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h @@ -764,6 +818,7 @@ sort.lo: config.h backtrace.h internal.h stest.lo: config.h backtrace.h internal.h state.lo: config.h backtrace.h backtrace-supported.h internal.h unknown.lo: config.h backtrace.h internal.h +xcoff.lo: config.h backtrace.h internal.h # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/libbacktrace/btest.c b/libbacktrace/btest.c index 0506d2b..ff29607 100644 --- a/libbacktrace/btest.c +++ b/libbacktrace/btest.c @@ -43,237 +43,7 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "backtrace.h" #include "backtrace-supported.h" -/* Portable attribute syntax. Actually some of these tests probably - won't work if the attributes are not recognized. */ - -#ifndef GCC_VERSION -# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) -#endif - -#if (GCC_VERSION < 2007) -# define __attribute__(x) -#endif - -#ifndef ATTRIBUTE_UNUSED -# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) -#endif - -/* Used to collect backtrace info. */ - -struct info -{ - char *filename; - int lineno; - char *function; -}; - -/* Passed to backtrace callback function. */ - -struct bdata -{ - struct info *all; - size_t index; - size_t max; - int failed; -}; - -/* Passed to backtrace_simple callback function. */ - -struct sdata -{ - uintptr_t *addrs; - size_t index; - size_t max; - int failed; -}; - -/* Passed to backtrace_syminfo callback function. */ - -struct symdata -{ - const char *name; - uintptr_t val, size; - int failed; -}; - -/* The backtrace state. */ - -static void *state; - -/* The number of failures. */ - -static int failures; - -/* Return the base name in a path. */ - -static const char * -base (const char *p) -{ - const char *last; - const char *s; - - last = NULL; - for (s = p; *s != '\0'; ++s) - { - if (IS_DIR_SEPARATOR (*s)) - last = s + 1; - } - return last != NULL ? last : p; -} - -/* Check an entry in a struct info array. */ - -static void -check (const char *name, int index, const struct info *all, int want_lineno, - const char *want_function, int *failed) -{ - if (*failed) - return; - if (all[index].filename == NULL || all[index].function == NULL) - { - fprintf (stderr, "%s: [%d]: missing file name or function name\n", - name, index); - *failed = 1; - return; - } - if (strcmp (base (all[index].filename), "btest.c") != 0) - { - fprintf (stderr, "%s: [%d]: got %s expected test.c\n", name, index, - all[index].filename); - *failed = 1; - } - if (all[index].lineno != want_lineno) - { - fprintf (stderr, "%s: [%d]: got %d expected %d\n", name, index, - all[index].lineno, want_lineno); - *failed = 1; - } - if (strcmp (all[index].function, want_function) != 0) - { - fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index, - all[index].function, want_function); - *failed = 1; - } -} - -/* The backtrace callback function. */ - -static int -callback_one (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED, - const char *filename, int lineno, const char *function) -{ - struct bdata *data = (struct bdata *) vdata; - struct info *p; - - if (data->index >= data->max) - { - fprintf (stderr, "callback_one: callback called too many times\n"); - data->failed = 1; - return 1; - } - - p = &data->all[data->index]; - if (filename == NULL) - p->filename = NULL; - else - { - p->filename = strdup (filename); - assert (p->filename != NULL); - } - p->lineno = lineno; - if (function == NULL) - p->function = NULL; - else - { - p->function = strdup (function); - assert (p->function != NULL); - } - ++data->index; - - return 0; -} - -/* An error callback passed to backtrace. */ - -static void -error_callback_one (void *vdata, const char *msg, int errnum) -{ - struct bdata *data = (struct bdata *) vdata; - - fprintf (stderr, "%s", msg); - if (errnum > 0) - fprintf (stderr, ": %s", strerror (errnum)); - fprintf (stderr, "\n"); - data->failed = 1; -} - -/* The backtrace_simple callback function. */ - -static int -callback_two (void *vdata, uintptr_t pc) -{ - struct sdata *data = (struct sdata *) vdata; - - if (data->index >= data->max) - { - fprintf (stderr, "callback_two: callback called too many times\n"); - data->failed = 1; - return 1; - } - - data->addrs[data->index] = pc; - ++data->index; - - return 0; -} - -/* An error callback passed to backtrace_simple. */ - -static void -error_callback_two (void *vdata, const char *msg, int errnum) -{ - struct sdata *data = (struct sdata *) vdata; - - fprintf (stderr, "%s", msg); - if (errnum > 0) - fprintf (stderr, ": %s", strerror (errnum)); - fprintf (stderr, "\n"); - data->failed = 1; -} - -/* The backtrace_syminfo callback function. */ - -static void -callback_three (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED, - const char *symname, uintptr_t symval, - uintptr_t symsize) -{ - struct symdata *data = (struct symdata *) vdata; - - if (symname == NULL) - data->name = NULL; - else - { - data->name = strdup (symname); - assert (data->name != NULL); - } - data->val = symval; - data->size = symsize; -} - -/* The backtrace_syminfo error callback function. */ - -static void -error_callback_three (void *vdata, const char *msg, int errnum) -{ - struct symdata *data = (struct symdata *) vdata; - - fprintf (stderr, "%s", msg); - if (errnum > 0) - fprintf (stderr, ": %s", strerror (errnum)); - fprintf (stderr, "\n"); - data->failed = 1; -} +#include "testlib.h" /* Test the backtrace function with non-inlined functions. */ @@ -325,9 +95,9 @@ f3 (int f1line, int f2line) data.failed = 1; } - check ("test1", 0, all, f3line, "f3", &data.failed); - check ("test1", 1, all, f2line, "f2", &data.failed); - check ("test1", 2, all, f1line, "test1", &data.failed); + check ("test1", 0, all, f3line, "f3", "btest.c", &data.failed); + check ("test1", 1, all, f2line, "f2", "btest.c", &data.failed); + check ("test1", 2, all, f1line, "test1", "btest.c", &data.failed); printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS"); @@ -377,9 +147,9 @@ f13 (int f1line, int f2line) data.failed = 1; } - check ("test2", 0, all, f3line, "f13", &data.failed); - check ("test2", 1, all, f2line, "f12", &data.failed); - check ("test2", 2, all, f1line, "test2", &data.failed); + check ("test2", 0, all, f3line, "f13", "btest.c", &data.failed); + check ("test2", 1, all, f2line, "f12", "btest.c", &data.failed); + check ("test2", 2, all, f1line, "test2", "btest.c", &data.failed); printf ("%s: backtrace_full inline\n", data.failed ? "FAIL" : "PASS"); @@ -462,9 +232,9 @@ f23 (int f1line, int f2line) } } - check ("test3", 0, all, f3line, "f23", &bdata.failed); - check ("test3", 1, all, f2line, "f22", &bdata.failed); - check ("test3", 2, all, f1line, "test3", &bdata.failed); + check ("test3", 0, all, f3line, "f23", "btest.c", &bdata.failed); + check ("test3", 1, all, f2line, "f22", "btest.c", &bdata.failed); + check ("test3", 2, all, f1line, "test3", "btest.c", &bdata.failed); if (bdata.failed) data.failed = 1; @@ -600,9 +370,9 @@ f33 (int f1line, int f2line) bdata.failed = 1; } - check ("test4", 0, all, f3line, "f33", &bdata.failed); - check ("test4", 1, all, f2line, "f32", &bdata.failed); - check ("test4", 2, all, f1line, "test4", &bdata.failed); + check ("test4", 0, all, f3line, "f33", "btest.c", &bdata.failed); + check ("test4", 1, all, f2line, "f32", "btest.c", &bdata.failed); + check ("test4", 2, all, f1line, "test4", "btest.c", &bdata.failed); if (bdata.failed) data.failed = 1; @@ -616,7 +386,7 @@ f33 (int f1line, int f2line) return failures; } -#if BACKTRACE_SUPPORTS_DATA +static int test5 (void) __attribute__ ((unused)); int global = 1; @@ -686,19 +456,6 @@ test5 (void) return failures; } -#endif /* BACKTRACE_SUPPORTS_DATA */ - -static void -error_callback_create (void *data ATTRIBUTE_UNUSED, const char *msg, - int errnum) -{ - fprintf (stderr, "%s", msg); - if (errnum > 0) - fprintf (stderr, ": %s", strerror (errnum)); - fprintf (stderr, "\n"); - exit (EXIT_FAILURE); -} - /* Run all the tests. */ int diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in index 87cb805..9fc7715 100644 --- a/libbacktrace/config.h.in +++ b/libbacktrace/config.h.in @@ -3,6 +3,9 @@ /* ELF size: 32 or 64 */ #undef BACKTRACE_ELF_SIZE +/* XCOFF size: 32 or 64 */ +#undef BACKTRACE_XCOFF_SIZE + /* Define to 1 if you have the __atomic functions */ #undef HAVE_ATOMIC_FUNCTIONS @@ -31,6 +34,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINK_H +/* Define if AIX loadquery is available. */ +#undef HAVE_LOADQUERY + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H @@ -49,6 +55,9 @@ /* Define to 1 if you have the __sync functions */ #undef HAVE_SYNC_FUNCTIONS +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_LDR_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MMAN_H @@ -123,6 +132,12 @@ #endif +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + /* Define to 1 if on MINIX. */ #undef _MINIX diff --git a/libbacktrace/configure b/libbacktrace/configure index 4e720e1..b7b88e2 100755 --- a/libbacktrace/configure +++ b/libbacktrace/configure @@ -604,6 +604,9 @@ LTLIBOBJS LIBOBJS NATIVE_FALSE NATIVE_TRUE +HAVE_PTHREAD_FALSE +HAVE_PTHREAD_TRUE +PTHREAD_CFLAGS BACKTRACE_USES_MALLOC ALLOC_FILE VIEW_FILE @@ -731,6 +734,7 @@ with_pic enable_fast_install with_gnu_ld enable_libtool_lock +enable_largefile with_system_libunwind enable_host_shared ' @@ -1371,6 +1375,7 @@ Optional Features: --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) + --disable-largefile omit support for large files --enable-host-shared build host code as shared libraries Optional Packages: @@ -11131,7 +11136,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11134 "configure" +#line 11139 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11237,7 +11242,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11240 "configure" +#line 11245 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11476,6 +11481,205 @@ CC="$lt_save_CC" +# Check whether --enable-largefile was given. +if test "${enable_largefile+set}" = set; then : + enableval=$enable_largefile; +fi + +if test "$enable_largefile" != no; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 +$as_echo_n "checking for special C compiler options needed for large files... " >&6; } +if test "${ac_cv_sys_largefile_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + break +fi +rm -f core conftest.err conftest.$ac_objext + CC="$CC -n32" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_largefile_CC=' -n32'; break +fi +rm -f core conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 +$as_echo "$ac_cv_sys_largefile_CC" >&6; } + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if test "${ac_cv_sys_file_offset_bits+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=64; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_file_offset_bits=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 +$as_echo "$ac_cv_sys_file_offset_bits" >&6; } +case $ac_cv_sys_file_offset_bits in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF +;; +esac +rm -rf conftest* + if test $ac_cv_sys_file_offset_bits = unknown; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 +$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } +if test "${ac_cv_sys_large_files+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=1; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_large_files=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 +$as_echo "$ac_cv_sys_large_files" >&6; } +case $ac_cv_sys_large_files in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF +;; +esac +rm -rf conftest* + fi +fi + + backtrace_supported=yes if test -n "${with_target_subdir}"; then @@ -11844,6 +12048,9 @@ elf*) FORMAT_FILE="elf.lo" ;; pecoff) FORMAT_FILE="pecoff.lo" backtrace_supports_data=no ;; +xcoff*) FORMAT_FILE="xcoff.lo" + backtrace_supports_data=no + ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not determine output file type" >&5 $as_echo "$as_me: WARNING: could not determine output file type" >&2;} FORMAT_FILE="unknown.lo" @@ -11865,6 +12072,19 @@ cat >>confdefs.h <<_ACEOF _ACEOF +# XCOFF defines. +xcoffsize= +case "$libbacktrace_cv_sys_filetype" in +xcoff32) xcoffsize=32 ;; +xcoff64) xcoffsize=64 ;; +*) xcoffsize=unused +esac + +cat >>confdefs.h <<_ACEOF +#define BACKTRACE_XCOFF_SIZE $xcoffsize +_ACEOF + + BACKTRACE_SUPPORTED=0 if test "$backtrace_supported" = "yes"; then BACKTRACE_SUPPORTED=1 @@ -12403,6 +12623,53 @@ $as_echo "#define HAVE_DL_ITERATE_PHDR 1" >>confdefs.h fi +# Check for loadquery. +for ac_header in sys/ldr.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "sys/ldr.h" "ac_cv_header_sys_ldr_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_ldr_h" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYS_LDR_H 1 +_ACEOF + +fi + +done + +if test "$ac_cv_header_sys_ldr_h" = "no"; then + have_loadquery=no +else + if test -n "${with_target_subdir}"; then + # When built as a GCC target library, we can't do a link test. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "loadquery" >/dev/null 2>&1; then : + have_loadquery=yes +else + have_loadquery=no +fi +rm -f conftest* + + else + ac_fn_c_check_func "$LINENO" "loadquery" "ac_cv_func_loadquery" +if test "x$ac_cv_func_loadquery" = x""yes; then : + have_loadquery=yes +else + have_loadquery=no +fi + + fi +fi +if test "$have_loadquery" = "yes"; then + +$as_echo "#define HAVE_LOADQUERY 1" >>confdefs.h + +fi + # Check for the fcntl function. if test -n "${with_target_subdir}"; then case "${host}" in @@ -12458,6 +12725,42 @@ $as_echo "#define HAVE_GETEXECNAME 1" >>confdefs.h fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -pthread is supported" >&5 +$as_echo_n "checking whether -pthread is supported... " >&6; } +if test "${libgo_cv_lib_pthread+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + CFLAGS_hold=$CFLAGS +CFLAGS="$CFLAGS -pthread" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int i; +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + libgo_cv_lib_pthread=yes +else + libgo_cv_lib_pthread=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +CFLAGS=$CFLAGS_hold +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libgo_cv_lib_pthread" >&5 +$as_echo "$libgo_cv_lib_pthread" >&6; } +PTHREAD_CFLAGS= +if test "$libgo_cv_lib_pthread" = yes; then + PTHREAD_CFLAGS=-pthread +fi + + + if test "$libgo_cv_lib_pthread" = yes; then + HAVE_PTHREAD_TRUE= + HAVE_PTHREAD_FALSE='#' +else + HAVE_PTHREAD_TRUE='#' + HAVE_PTHREAD_FALSE= +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether tests can run" >&5 $as_echo_n "checking whether tests can run... " >&6; } if test "${libbacktrace_cv_sys_native+set}" = set; then : @@ -12620,6 +12923,10 @@ if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then as_fn_error "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${HAVE_PTHREAD_TRUE}" && test -z "${HAVE_PTHREAD_FALSE}"; then + as_fn_error "conditional \"HAVE_PTHREAD\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${NATIVE_TRUE}" && test -z "${NATIVE_FALSE}"; then as_fn_error "conditional \"NATIVE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac index 71e8518..03e33c5 100644 --- a/libbacktrace/configure.ac +++ b/libbacktrace/configure.ac @@ -82,6 +82,8 @@ esac LT_INIT AM_PROG_LIBTOOL +AC_SYS_LARGEFILE + backtrace_supported=yes if test -n "${with_target_subdir}"; then @@ -231,6 +233,9 @@ elf*) FORMAT_FILE="elf.lo" ;; pecoff) FORMAT_FILE="pecoff.lo" backtrace_supports_data=no ;; +xcoff*) FORMAT_FILE="xcoff.lo" + backtrace_supports_data=no + ;; *) AC_MSG_WARN([could not determine output file type]) FORMAT_FILE="unknown.lo" backtrace_supported=no @@ -247,6 +252,15 @@ elf64) elfsize=64 ;; esac AC_DEFINE_UNQUOTED([BACKTRACE_ELF_SIZE], [$elfsize], [ELF size: 32 or 64]) +# XCOFF defines. +xcoffsize= +case "$libbacktrace_cv_sys_filetype" in +xcoff32) xcoffsize=32 ;; +xcoff64) xcoffsize=64 ;; +*) xcoffsize=unused +esac +AC_DEFINE_UNQUOTED([BACKTRACE_XCOFF_SIZE], [$xcoffsize], [XCOFF size: 32 or 64]) + BACKTRACE_SUPPORTED=0 if test "$backtrace_supported" = "yes"; then BACKTRACE_SUPPORTED=1 @@ -325,6 +339,24 @@ if test "$have_dl_iterate_phdr" = "yes"; then AC_DEFINE(HAVE_DL_ITERATE_PHDR, 1, [Define if dl_iterate_phdr is available.]) fi +# Check for loadquery. +AC_CHECK_HEADERS(sys/ldr.h) +if test "$ac_cv_header_sys_ldr_h" = "no"; then + have_loadquery=no +else + if test -n "${with_target_subdir}"; then + # When built as a GCC target library, we can't do a link test. + AC_EGREP_HEADER([loadquery], [sys/ldr.h], [have_loadquery=yes], + [have_loadquery=no]) + else + AC_CHECK_FUNC([loadquery], [have_loadquery=yes], + [have_loadquery=no]) + fi +fi +if test "$have_loadquery" = "yes"; then + AC_DEFINE(HAVE_LOADQUERY, 1, [Define if AIX loadquery is available.]) +fi + # Check for the fcntl function. if test -n "${with_target_subdir}"; then case "${host}" in @@ -355,6 +387,23 @@ if test "$have_getexecname" = "yes"; then AC_DEFINE(HAVE_GETEXECNAME, 1, [Define if getexecname is available.]) fi +dnl Test whether the compiler supports the -pthread option. +AC_CACHE_CHECK([whether -pthread is supported], +[libgo_cv_lib_pthread], +[CFLAGS_hold=$CFLAGS +CFLAGS="$CFLAGS -pthread" +AC_COMPILE_IFELSE([[int i;]], +[libgo_cv_lib_pthread=yes], +[libgo_cv_lib_pthread=no]) +CFLAGS=$CFLAGS_hold]) +PTHREAD_CFLAGS= +if test "$libgo_cv_lib_pthread" = yes; then + PTHREAD_CFLAGS=-pthread +fi +AC_SUBST(PTHREAD_CFLAGS) + +AM_CONDITIONAL(HAVE_PTHREAD, test "$libgo_cv_lib_pthread" = yes) + AC_CACHE_CHECK([whether tests can run], [libbacktrace_cv_sys_native], [AC_RUN_IFELSE([AC_LANG_PROGRAM([], [return 0;])], diff --git a/libbacktrace/dwarf.c b/libbacktrace/dwarf.c index 55b8d7d..e1cb47e 100644 --- a/libbacktrace/dwarf.c +++ b/libbacktrace/dwarf.c @@ -1563,16 +1563,15 @@ add_line (struct backtrace_state *state, struct dwarf_data *ddata, return 1; } -/* Free the line header information. If FREE_FILENAMES is true we - free the file names themselves, otherwise we leave them, as there - may be line structures pointing to them. */ +/* Free the line header information. */ static void free_line_header (struct backtrace_state *state, struct line_header *hdr, backtrace_error_callback error_callback, void *data) { - backtrace_free (state, hdr->dirs, hdr->dirs_count * sizeof (const char *), - error_callback, data); + if (hdr->dirs_count != 0) + backtrace_free (state, hdr->dirs, hdr->dirs_count * sizeof (const char *), + error_callback, data); backtrace_free (state, hdr->filenames, hdr->filenames_count * sizeof (char *), error_callback, data); @@ -1633,12 +1632,16 @@ read_line_header (struct backtrace_state *state, struct unit *u, ++hdr->dirs_count; } - hdr->dirs = ((const char **) - backtrace_alloc (state, - hdr->dirs_count * sizeof (const char *), - line_buf->error_callback, line_buf->data)); - if (hdr->dirs == NULL) - return 0; + hdr->dirs = NULL; + if (hdr->dirs_count != 0) + { + hdr->dirs = ((const char **) + backtrace_alloc (state, + hdr->dirs_count * sizeof (const char *), + line_buf->error_callback, line_buf->data)); + if (hdr->dirs == NULL) + return 0; + } i = 0; while (*hdr_buf.buf != '\0') diff --git a/libbacktrace/edtest.c b/libbacktrace/edtest.c new file mode 100644 index 0000000..54c705c --- /dev/null +++ b/libbacktrace/edtest.c @@ -0,0 +1,121 @@ +/* edtest.c -- Test for libbacktrace storage allocation stress handling + Copyright (C) 2017 Free Software Foundation, Inc. + +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. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +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. */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "backtrace.h" +#include "backtrace-supported.h" +#include "internal.h" + +#include "testlib.h" + +static int test1 (void) __attribute__ ((noinline, unused)); +static int test1 (void) __attribute__ ((noinline, unused)); +extern int f2 (int); +extern int f3 (int, int); + +static int +test1 (void) +{ + /* Returning a value here and elsewhere avoids a tailcall which + would mess up the backtrace. */ + return f2 (__LINE__) + 1; +} + +int +f3 (int f1line, int f2line) +{ + struct info all[20]; + struct bdata data; + int f3line; + int i; + + data.all = &all[0]; + data.index = 0; + data.max = 20; + data.failed = 0; + + f3line = __LINE__ + 1; + i = backtrace_full (state, 0, callback_one, error_callback_one, &data); + + if (i != 0) + { + fprintf (stderr, "test1: unexpected return value %d\n", i); + data.failed = 1; + } + + if (data.index < 3) + { + fprintf (stderr, + "test1: not enough frames; got %zu, expected at least 3\n", + data.index); + data.failed = 1; + } + + check ("test1", 0, all, f3line, "f3", "edtest.c", &data.failed); + check ("test1", 1, all, f2line, "f2", "edtest2_build.c", &data.failed); + check ("test1", 2, all, f1line, "test1", "edtest.c", &data.failed); + + printf ("%s: backtrace_full alloc stress\n", data.failed ? "FAIL" : "PASS"); + + if (data.failed) + ++failures; + + return failures; +} + +int +main (int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) +{ + state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, + error_callback_create, NULL); + + // Grab the storage allocation lock prior to doing anything interesting. + // The intent here is to insure that the backtrace_alloc code is forced + // to always call mmap() for new memory as opposed to reusing previously + // allocated memory from the free list. Doing things this way helps + // simulate what you might see in a multithreaded program in which there + // are racing calls to the allocator. + struct backtrace_state *state_internal = + (struct backtrace_state *) state; + state_internal->lock_alloc = 1; + + // Kick off the test + test1(); + + exit (failures > 0 ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/libbacktrace/edtest2.c b/libbacktrace/edtest2.c new file mode 100644 index 0000000..1ab78ee --- /dev/null +++ b/libbacktrace/edtest2.c @@ -0,0 +1,43 @@ +/* edtest2.c -- Test for libbacktrace storage allocation stress handling (p2) + Copyright (C) 2017 Free Software Foundation, Inc. + +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. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +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. */ + +/* This file intentionally written without any #include's + */ + +extern int f3(int, int); +extern int f2(int); + +int f2(int x) +{ + /* Returning a value here and elsewhere avoids a tailcall which + would mess up the backtrace. */ + return f3(x, __LINE__) + 3; +} diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c index 81ba344..ed6f4e1 100644 --- a/libbacktrace/elf.c +++ b/libbacktrace/elf.c @@ -70,7 +70,7 @@ dl_iterate_phdr (int (*callback) (struct dl_phdr_info *, ELF. We could make this code test and support either possibility, but there is no point. This code only works for the currently running executable, which means that we know the ELF mode at - configure mode. */ + configure time. */ #if BACKTRACE_ELF_SIZE != 32 && BACKTRACE_ELF_SIZE != 64 #error "Unknown BACKTRACE_ELF_SIZE" @@ -962,18 +962,12 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, } if (!state->threaded) - { - if (state->fileline_fn == NULL || state->fileline_fn == elf_nodebug) - *fileline_fn = elf_fileline_fn; - } + *fileline_fn = state->fileline_fn; else - { - fileline current_fn; + *fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn); - current_fn = backtrace_atomic_load_pointer (&state->fileline_fn); - if (current_fn == NULL || current_fn == elf_nodebug) - *fileline_fn = elf_fileline_fn; - } + if (*fileline_fn == NULL || *fileline_fn == elf_nodebug) + *fileline_fn = elf_fileline_fn; return 1; } diff --git a/libbacktrace/fileline.c b/libbacktrace/fileline.c index 27ebbed..674fbba 100644 --- a/libbacktrace/fileline.c +++ b/libbacktrace/fileline.c @@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE. */ #include #include #include +#include #include "backtrace.h" #include "internal.h" @@ -57,6 +58,7 @@ fileline_initialize (struct backtrace_state *state, int pass; int called_error_callback; int descriptor; + char buf[64]; if (!state->threaded) failed = state->fileline_initialization_failed; @@ -80,7 +82,7 @@ fileline_initialize (struct backtrace_state *state, descriptor = -1; called_error_callback = 0; - for (pass = 0; pass < 4; ++pass) + for (pass = 0; pass < 5; ++pass) { const char *filename; int does_not_exist; @@ -99,6 +101,11 @@ fileline_initialize (struct backtrace_state *state, case 3: filename = "/proc/curproc/file"; break; + case 4: + snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out", + (long) getpid ()); + filename = buf; + break; default: abort (); } diff --git a/libbacktrace/filetype.awk b/libbacktrace/filetype.awk index 57bab79..cf6e1b6 100644 --- a/libbacktrace/filetype.awk +++ b/libbacktrace/filetype.awk @@ -3,3 +3,6 @@ /\177ELF\002/ { if (NR == 1) { print "elf64"; exit } } /\114\001/ { if (NR == 1) { print "pecoff"; exit } } /\144\206/ { if (NR == 1) { print "pecoff"; exit } } +/\001\337/ { if (NR == 1) { print "xcoff32"; exit } } +/\001\367/ { if (NR == 1) { print "xcoff64"; exit } } + diff --git a/libbacktrace/testlib.c b/libbacktrace/testlib.c new file mode 100644 index 0000000..05420ca --- /dev/null +++ b/libbacktrace/testlib.c @@ -0,0 +1,234 @@ +/* testlib.c -- test functions for libbacktrace library + Copyright (C) 2012-2017 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +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. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +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. */ + +#include +#include +#include +#include + +#include "filenames.h" + +#include "backtrace.h" + +#include "testlib.h" + +/* The backtrace state. */ + +void *state; + +/* The number of failures. */ + +int failures; + +/* Return the base name in a path. */ + +const char * +base (const char *p) +{ + const char *last; + const char *s; + + last = NULL; + for (s = p; *s != '\0'; ++s) + { + if (IS_DIR_SEPARATOR (*s)) + last = s + 1; + } + return last != NULL ? last : p; +} + +/* Check an entry in a struct info array. */ + +void +check (const char *name, int index, const struct info *all, int want_lineno, + const char *want_function, const char *want_file, int *failed) +{ + if (*failed) + return; + if (all[index].filename == NULL || all[index].function == NULL) + { + fprintf (stderr, "%s: [%d]: missing file name or function name\n", + name, index); + *failed = 1; + return; + } + if (strcmp (base (all[index].filename), want_file) != 0) + { + fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index, + all[index].filename, want_file); + *failed = 1; + } + if (all[index].lineno != want_lineno) + { + fprintf (stderr, "%s: [%d]: got %d expected %d\n", name, index, + all[index].lineno, want_lineno); + *failed = 1; + } + if (strcmp (all[index].function, want_function) != 0) + { + fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index, + all[index].function, want_function); + *failed = 1; + } +} + +/* The backtrace callback function. */ + +int +callback_one (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED, + const char *filename, int lineno, const char *function) +{ + struct bdata *data = (struct bdata *) vdata; + struct info *p; + + if (data->index >= data->max) + { + fprintf (stderr, "callback_one: callback called too many times\n"); + data->failed = 1; + return 1; + } + + p = &data->all[data->index]; + if (filename == NULL) + p->filename = NULL; + else + { + p->filename = strdup (filename); + assert (p->filename != NULL); + } + p->lineno = lineno; + if (function == NULL) + p->function = NULL; + else + { + p->function = strdup (function); + assert (p->function != NULL); + } + ++data->index; + + return 0; +} + +/* An error callback passed to backtrace. */ + +void +error_callback_one (void *vdata, const char *msg, int errnum) +{ + struct bdata *data = (struct bdata *) vdata; + + fprintf (stderr, "%s", msg); + if (errnum > 0) + fprintf (stderr, ": %s", strerror (errnum)); + fprintf (stderr, "\n"); + data->failed = 1; +} + +/* The backtrace_simple callback function. */ + +int +callback_two (void *vdata, uintptr_t pc) +{ + struct sdata *data = (struct sdata *) vdata; + + if (data->index >= data->max) + { + fprintf (stderr, "callback_two: callback called too many times\n"); + data->failed = 1; + return 1; + } + + data->addrs[data->index] = pc; + ++data->index; + + return 0; +} + +/* An error callback passed to backtrace_simple. */ + +void +error_callback_two (void *vdata, const char *msg, int errnum) +{ + struct sdata *data = (struct sdata *) vdata; + + fprintf (stderr, "%s", msg); + if (errnum > 0) + fprintf (stderr, ": %s", strerror (errnum)); + fprintf (stderr, "\n"); + data->failed = 1; +} + +/* The backtrace_syminfo callback function. */ + +void +callback_three (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED, + const char *symname, uintptr_t symval, + uintptr_t symsize) +{ + struct symdata *data = (struct symdata *) vdata; + + if (symname == NULL) + data->name = NULL; + else + { + data->name = strdup (symname); + assert (data->name != NULL); + } + data->val = symval; + data->size = symsize; +} + +/* The backtrace_syminfo error callback function. */ + +void +error_callback_three (void *vdata, const char *msg, int errnum) +{ + struct symdata *data = (struct symdata *) vdata; + + fprintf (stderr, "%s", msg); + if (errnum > 0) + fprintf (stderr, ": %s", strerror (errnum)); + fprintf (stderr, "\n"); + data->failed = 1; +} + +/* The backtrace_create_state error callback function. */ + +void +error_callback_create (void *data ATTRIBUTE_UNUSED, const char *msg, + int errnum) +{ + fprintf (stderr, "%s", msg); + if (errnum > 0) + fprintf (stderr, ": %s", strerror (errnum)); + fprintf (stderr, "\n"); + exit (EXIT_FAILURE); +} diff --git a/libbacktrace/testlib.h b/libbacktrace/testlib.h new file mode 100644 index 0000000..2bfe62f --- /dev/null +++ b/libbacktrace/testlib.h @@ -0,0 +1,110 @@ +/* testlib.h -- Header for test functions for libbacktrace library + Copyright (C) 2012-2017 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +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. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +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 LIBBACKTRACE_TESTLIB_H +#define LIBBACKTRACE_TESTLIB_H + +/* Portable attribute syntax. Actually some of these tests probably + won't work if the attributes are not recognized. */ + +#ifndef GCC_VERSION +# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) +#endif + +#if (GCC_VERSION < 2007) +# define __attribute__(x) +#endif + +#ifndef ATTRIBUTE_UNUSED +# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif + +/* Used to collect backtrace info. */ + +struct info +{ + char *filename; + int lineno; + char *function; +}; + +/* Passed to backtrace callback function. */ + +struct bdata +{ + struct info *all; + size_t index; + size_t max; + int failed; +}; + +/* Passed to backtrace_simple callback function. */ + +struct sdata +{ + uintptr_t *addrs; + size_t index; + size_t max; + int failed; +}; + +/* Passed to backtrace_syminfo callback function. */ + +struct symdata +{ + const char *name; + uintptr_t val, size; + int failed; +}; + +/* The backtrace state. */ + +extern void *state; + +/* The number of failures. */ + +extern int failures; + +extern const char *base (const char *p); +extern void check (const char *name, int index, const struct info *all, + int want_lineno, const char *want_function, + const char *want_file, int *failed); +extern int callback_one (void *, uintptr_t, const char *, int, const char *); +extern void error_callback_one (void *, const char *, int); +extern int callback_two (void *, uintptr_t); +extern void error_callback_two (void *, const char *, int); +extern void callback_three (void *, uintptr_t, const char *, uintptr_t, + uintptr_t); +extern void error_callback_three (void *, const char *, int); +extern void error_callback_create (void *, const char *, int); + +#endif /* !defined(LIBBACKTRACE_TESTLIB_H) */ diff --git a/libbacktrace/ttest.c b/libbacktrace/ttest.c new file mode 100644 index 0000000..6c2d26c --- /dev/null +++ b/libbacktrace/ttest.c @@ -0,0 +1,161 @@ +/* ttest.c -- Test for libbacktrace library + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +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. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +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. */ + +/* Test using the libbacktrace library from multiple threads. */ + +#include +#include +#include +#include + +#include + +#include "filenames.h" + +#include "backtrace.h" +#include "backtrace-supported.h" + +#include "testlib.h" + +static int f2 (int) __attribute__ ((noinline)); +static int f3 (int, int) __attribute__ ((noinline)); + +/* Test that a simple backtrace works. This is called via + pthread_create. It returns the number of failures, as void *. */ + +static void * +test1_thread (void *arg ATTRIBUTE_UNUSED) +{ + /* Returning a value here and elsewhere avoids a tailcall which + would mess up the backtrace. */ + return (void *) (uintptr_t) (f2 (__LINE__) - 2); +} + +static int +f2 (int f1line) +{ + return f3 (f1line, __LINE__) + 2; +} + +static int +f3 (int f1line, int f2line) +{ + struct info all[20]; + struct bdata data; + int f3line; + int i; + + data.all = &all[0]; + data.index = 0; + data.max = 20; + data.failed = 0; + + f3line = __LINE__ + 1; + i = backtrace_full (state, 0, callback_one, error_callback_one, &data); + + if (i != 0) + { + fprintf (stderr, "test1: unexpected return value %d\n", i); + data.failed = 1; + } + + if (data.index < 3) + { + fprintf (stderr, + "test1: not enough frames; got %zu, expected at least 3\n", + data.index); + data.failed = 1; + } + + check ("test1", 0, all, f3line, "f3", "ttest.c", &data.failed); + check ("test1", 1, all, f2line, "f2", "ttest.c", &data.failed); + check ("test1", 2, all, f1line, "test1_thread", "ttest.c", &data.failed); + + return data.failed; +} + +/* Run the test with 10 threads simultaneously. */ + +#define THREAD_COUNT 10 + +static void test1 (void) __attribute__ ((unused)); + +static void +test1 (void) +{ + pthread_t atid[THREAD_COUNT]; + int i; + int errnum; + int this_fail; + void *ret; + + for (i = 0; i < THREAD_COUNT; i++) + { + errnum = pthread_create (&atid[i], NULL, test1_thread, NULL); + if (errnum != 0) + { + fprintf (stderr, "pthread_create %d: %s\n", i, strerror (errnum)); + exit (EXIT_FAILURE); + } + } + + this_fail = 0; + for (i = 0; i < THREAD_COUNT; i++) + { + errnum = pthread_join (atid[i], &ret); + if (errnum != 0) + { + fprintf (stderr, "pthread_join %d: %s\n", i, strerror (errnum)); + exit (EXIT_FAILURE); + } + this_fail += (int) (uintptr_t) ret; + } + + printf ("%s: threaded backtrace_full noinline\n", this_fail > 0 ? "FAIL" : "PASS"); + + failures += this_fail; +} + +int +main (int argc ATTRIBUTE_UNUSED, char **argv) +{ + state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, + error_callback_create, NULL); + +#if BACKTRACE_SUPPORTED +#if BACKTRACE_SUPPORTS_THREADS + test1 (); +#endif +#endif + + exit (failures ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/libbacktrace/xcoff.c b/libbacktrace/xcoff.c new file mode 100644 index 0000000..b3d7e24 --- /dev/null +++ b/libbacktrace/xcoff.c @@ -0,0 +1,1485 @@ +/* xcoff.c -- Get debug data from an XCOFF file for backtraces. + Copyright (C) 2012-2017 Free Software Foundation, Inc. + Adapted from elf.c. + +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. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +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. */ + +#include "config.h" + +#include +#include +#include +#include + +#ifdef HAVE_LOADQUERY +#include +#endif + +#include "backtrace.h" +#include "internal.h" + +/* The configure script must tell us whether we are 32-bit or 64-bit + XCOFF. We could make this code test and support either possibility, + but there is no point. This code only works for the currently + running executable, which means that we know the XCOFF mode at + configure time. */ + +#if BACKTRACE_XCOFF_SIZE != 32 && BACKTRACE_XCOFF_SIZE != 64 +#error "Unknown BACKTRACE_XCOFF_SIZE" +#endif + +/* XCOFF file header. */ + +#if BACKTRACE_XCOFF_SIZE == 32 + +typedef struct { + uint16_t f_magic; + uint16_t f_nscns; + uint32_t f_timdat; + uint32_t f_symptr; + uint32_t f_nsyms; + uint16_t f_opthdr; + uint16_t f_flags; +} b_xcoff_filhdr; + +#define XCOFF_MAGIC 0737 + +#else /* BACKTRACE_XCOFF_SIZE != 32 */ + +typedef struct { + uint16_t f_magic; + uint16_t f_nscns; + uint32_t f_timdat; + uint64_t f_symptr; + uint16_t f_opthdr; + uint16_t f_flags; + uint32_t f_nsyms; +} b_xcoff_filhdr; + +#define XCOFF_MAGIC 0767 + +#endif /* BACKTRACE_XCOFF_SIZE != 32 */ + +#define F_SHROBJ 0x2000 /* File is a shared object. */ + +/* XCOFF section header. */ + +#if BACKTRACE_XCOFF_SIZE == 32 + +typedef struct { + char s_name[8]; + uint32_t s_paddr; + uint32_t s_vaddr; + uint32_t s_size; + uint32_t s_scnptr; + uint32_t s_relptr; + uint32_t s_lnnoptr; + uint16_t s_nreloc; + uint16_t s_nlnno; + uint32_t s_flags; +} b_xcoff_scnhdr; + +#define _OVERFLOW_MARKER 65535 + +#else /* BACKTRACE_XCOFF_SIZE != 32 */ + +typedef struct { + char name[8]; + uint64_t s_paddr; + uint64_t s_vaddr; + uint64_t s_size; + uint64_t s_scnptr; + uint64_t s_relptr; + uint64_t s_lnnoptr; + uint32_t s_nreloc; + uint32_t s_nlnno; + uint32_t s_flags; +} b_xcoff_scnhdr; + +#endif /* BACKTRACE_XCOFF_SIZE != 32 */ + +#define STYP_TEXT 0x20 /* Executable text (code) section. */ +#define STYP_OVRFLO 0x8000 /* Line-number field overflow section. */ + +/* XCOFF symbol. */ + +#define SYMNMLEN 8 + +#if BACKTRACE_XCOFF_SIZE == 32 + +typedef struct { + union { + char _name[SYMNMLEN]; + struct { + uint32_t _zeroes; + uint32_t _offset; + } _s; + } _u; +#define n_name _u._name +#define n_zeroes _u._s._zeroes +#define n_offset_ _u._s._offset + + uint32_t n_value; + int16_t n_scnum; + uint16_t n_type; + uint8_t n_sclass; + uint8_t n_numaux; +} __attribute__ ((packed)) b_xcoff_syment; + +#else /* BACKTRACE_XCOFF_SIZE != 32 */ + +typedef struct { + uint64_t n_value; + uint32_t n_offset_; + int16_t n_scnum; + uint16_t n_type; + uint8_t n_sclass; + uint8_t n_numaux; +} __attribute__ ((packed)) b_xcoff_syment; + +#endif /* BACKTRACE_XCOFF_SIZE != 32 */ + +#define SYMESZ 18 + +#define C_EXT 2 /* External symbol. */ +#define C_FCN 101 /* Beginning or end of function. */ +#define C_FILE 103 /* Source file name. */ +#define C_HIDEXT 107 /* Unnamed external symbol. */ +#define C_BINCL 108 /* Beginning of include file. */ +#define C_EINCL 109 /* End of include file. */ +#define C_WEAKEXT 111 /* Weak external symbol. */ + +#define ISFCN(x) ((x) & 0x0020) + +/* XCOFF AUX entry. */ + +#define AUXESZ 18 +#define FILNMLEN 14 + +typedef union { +#if BACKTRACE_XCOFF_SIZE == 32 + struct { + uint16_t pad; + uint16_t x_lnnohi; + uint16_t x_lnno; + } x_block; +#else + struct { + uint32_t x_lnno; + } x_block; +#endif + union { + char x_fname[FILNMLEN]; + struct { + uint32_t x_zeroes; + uint32_t x_offset; + char pad[FILNMLEN-8]; + uint8_t x_ftype; + } _x; + } x_file; +#if BACKTRACE_XCOFF_SIZE == 32 + struct { + uint32_t x_exptr; + uint32_t x_fsize; + uint32_t x_lnnoptr; + uint32_t x_endndx; + } x_fcn; +#else + struct { + uint64_t x_lnnoptr; + uint32_t x_fsize; + uint32_t x_endndx; + } x_fcn; +#endif + struct { + uint8_t pad[AUXESZ-1]; + uint8_t x_auxtype; + } x_auxtype; +} __attribute__ ((packed)) b_xcoff_auxent; + +/* XCOFF line number entry. */ + +#if BACKTRACE_XCOFF_SIZE == 32 + +typedef struct { + union { + uint32_t l_symndx; + uint32_t l_paddr; + } l_addr; + uint16_t l_lnno; +} b_xcoff_lineno; + +#define LINESZ 6 + +#else /* BACKTRACE_XCOFF_SIZE != 32 */ + +typedef struct { + union { + uint32_t l_symndx; + uint64_t l_paddr; + } l_addr; + uint32_t l_lnno; +} b_xcoff_lineno; + +#define LINESZ 12 + +#endif /* BACKTRACE_XCOFF_SIZE != 32 */ + +#if BACKTRACE_XCOFF_SIZE == 32 +#define XCOFF_AIX_TEXTBASE 0x10000000u +#else +#define XCOFF_AIX_TEXTBASE 0x100000000ul +#endif + +/* AIX big archive fixed-length header. */ + +#define AIAMAGBIG "\n" + +typedef struct { + char fl_magic[8]; /* Archive magic string. */ + char fl_memoff[20]; /* Offset to member table. */ + char fl_gstoff[20]; /* Offset to global symbol table. */ + char fl_gst64off[20]; /* Offset to global symbol table for 64-bit objects. */ + char fl_fstmoff[20]; /* Offset to first archive member. */ + char fl_freeoff[20]; /* Offset to first member on free list. */ +} b_ar_fl_hdr; + +/* AIX big archive file member header. */ + +typedef struct { + char ar_size[20]; /* File member size - decimal. */ + char ar_nxtmem[20]; /* Next member offset - decimal. */ + char ar_prvmem[20]; /* Previous member offset - decimal. */ + char ar_date[12]; /* File member date - decimal. */ + char ar_uid[12]; /* File member userid - decimal. */ + char ar_gid[12]; /* File member group id - decimal. */ + char ar_mode[12]; /* File member mode - octal. */ + char ar_namlen[4]; /* File member name length - decimal. */ + char ar_name[2]; /* Start of member name. */ +} b_ar_hdr; + + +/* Information we keep for an XCOFF symbol. */ + +struct xcoff_symbol +{ + /* The name of the symbol. */ + const char *name; + /* The address of the symbol. */ + uintptr_t address; + /* The size of the symbol. */ + size_t size; +}; + +/* Information to pass to xcoff_syminfo. */ + +struct xcoff_syminfo_data +{ + /* Symbols for the next module. */ + struct xcoff_syminfo_data *next; + /* The XCOFF symbols, sorted by address. */ + struct xcoff_symbol *symbols; + /* The number of symbols. */ + size_t count; +}; + +/* Information about an include file. */ + +struct xcoff_incl +{ + /* File name. */ + const char *filename; + /* Offset to first line number from the include file. */ + uintptr_t begin; + /* Offset to last line number from the include file. */ + uintptr_t end; +}; + +/* A growable vector of include files information. */ + +struct xcoff_incl_vector +{ + /* Memory. This is an array of struct xcoff_incl. */ + struct backtrace_vector vec; + /* Number of include files. */ + size_t count; +}; + +/* Map a single PC value to a file/function/line. */ + +struct xcoff_line +{ + /* PC. */ + uintptr_t pc; + /* File name. Many entries in the array are expected to point to + the same file name. */ + const char *filename; + /* Function name. */ + const char *function; + /* Line number. */ + int lineno; +}; + +/* A growable vector of line number information. This is used while + reading the line numbers. */ + +struct xcoff_line_vector +{ + /* Memory. This is an array of struct xcoff_line. */ + struct backtrace_vector vec; + /* Number of valid mappings. */ + size_t count; +}; + +/* The information we need to map a PC to a file and line. */ + +struct xcoff_fileline_data +{ + /* The data for the next file we know about. */ + struct xcoff_fileline_data *next; + /* Line number information. */ + struct xcoff_line_vector vec; +}; + + +/* A dummy callback function used when we can't find any debug info. */ + +static int +xcoff_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED, + uintptr_t pc ATTRIBUTE_UNUSED, + backtrace_full_callback callback ATTRIBUTE_UNUSED, + backtrace_error_callback error_callback, void *data) +{ + error_callback (data, "no debug info in XCOFF executable", -1); + return 0; +} + +/* A dummy callback function used when we can't find a symbol + table. */ + +static void +xcoff_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED, + uintptr_t addr ATTRIBUTE_UNUSED, + backtrace_syminfo_callback callback ATTRIBUTE_UNUSED, + backtrace_error_callback error_callback, void *data) +{ + error_callback (data, "no symbol table in XCOFF executable", -1); +} + +/* Compare struct xcoff_symbol for qsort. */ + +static int +xcoff_symbol_compare (const void *v1, const void *v2) +{ + const struct xcoff_symbol *e1 = (const struct xcoff_symbol *) v1; + const struct xcoff_symbol *e2 = (const struct xcoff_symbol *) v2; + + if (e1->address < e2->address) + return -1; + else if (e1->address > e2->address) + return 1; + else + return 0; +} + +/* Compare an ADDR against an xcoff_symbol for bsearch. */ + +static int +xcoff_symbol_search (const void *vkey, const void *ventry) +{ + const uintptr_t *key = (const uintptr_t *) vkey; + const struct xcoff_symbol *entry = (const struct xcoff_symbol *) ventry; + uintptr_t addr; + + addr = *key; + if (addr < entry->address) + return -1; + else if ((entry->size == 0 && addr > entry->address) + || (entry->size > 0 && addr >= entry->address + entry->size)) + return 1; + else + return 0; +} + +/* Add XDATA to the list in STATE. */ + +static void +xcoff_add_syminfo_data (struct backtrace_state *state, + struct xcoff_syminfo_data *xdata) +{ + if (!state->threaded) + { + struct xcoff_syminfo_data **pp; + + for (pp = (struct xcoff_syminfo_data **) (void *) &state->syminfo_data; + *pp != NULL; + pp = &(*pp)->next) + ; + *pp = xdata; + } + else + { + while (1) + { + struct xcoff_syminfo_data **pp; + + pp = (struct xcoff_syminfo_data **) (void *) &state->syminfo_data; + + while (1) + { + struct xcoff_syminfo_data *p; + + p = backtrace_atomic_load_pointer (pp); + + if (p == NULL) + break; + + pp = &p->next; + } + + if (__sync_bool_compare_and_swap (pp, NULL, xdata)) + break; + } + } +} + +/* Return the symbol name and value for an ADDR. */ + +static void +xcoff_syminfo (struct backtrace_state *state ATTRIBUTE_UNUSED, uintptr_t addr, + backtrace_syminfo_callback callback, + backtrace_error_callback error_callback ATTRIBUTE_UNUSED, + void *data) +{ + struct xcoff_syminfo_data *edata; + struct xcoff_symbol *sym = NULL; + + if (!state->threaded) + { + for (edata = (struct xcoff_syminfo_data *) state->syminfo_data; + edata != NULL; + edata = edata->next) + { + sym = ((struct xcoff_symbol *) + bsearch (&addr, edata->symbols, edata->count, + sizeof (struct xcoff_symbol), xcoff_symbol_search)); + if (sym != NULL) + break; + } + } + else + { + struct xcoff_syminfo_data **pp; + + pp = (struct xcoff_syminfo_data **) (void *) &state->syminfo_data; + while (1) + { + edata = backtrace_atomic_load_pointer (pp); + if (edata == NULL) + break; + + sym = ((struct xcoff_symbol *) + bsearch (&addr, edata->symbols, edata->count, + sizeof (struct xcoff_symbol), xcoff_symbol_search)); + if (sym != NULL) + break; + + pp = &edata->next; + } + } + + if (sym == NULL) + callback (data, addr, NULL, 0, 0); + else + callback (data, addr, sym->name, sym->address, sym->size); +} + +/* Return the name of an XCOFF symbol. */ + +static const char * +xcoff_symname (const b_xcoff_syment *asym, + const unsigned char *strtab, size_t strtab_size) +{ +#if BACKTRACE_XCOFF_SIZE == 32 + if (asym->n_zeroes != 0) + { + /* Make a copy as we will release the symtab view. */ + char name[SYMNMLEN+1]; + strncpy (name, asym->n_name, SYMNMLEN); + name[SYMNMLEN] = '\0'; + return strdup (name); + } +#endif + if (asym->n_sclass & 0x80) + return NULL; /* .debug */ + if (asym->n_offset_ >= strtab_size) + return NULL; + return (const char *) strtab + asym->n_offset_; +} + +/* Initialize the symbol table info for xcoff_syminfo. */ + +static int +xcoff_initialize_syminfo (struct backtrace_state *state, + uintptr_t base_address, + const b_xcoff_scnhdr *sects, + const b_xcoff_syment *syms, size_t nsyms, + const unsigned char *strtab, size_t strtab_size, + backtrace_error_callback error_callback, void *data, + struct xcoff_syminfo_data *sdata) +{ + size_t xcoff_symbol_count; + size_t xcoff_symbol_size; + struct xcoff_symbol *xcoff_symbols; + size_t i; + unsigned int j; + + /* We only care about function symbols. Count them. */ + xcoff_symbol_count = 0; + for (i = 0; i < nsyms; ++i) + { + const b_xcoff_syment *asym = &syms[i]; + if ((asym->n_sclass == C_EXT || asym->n_sclass == C_HIDEXT + || asym->n_sclass == C_WEAKEXT) + && ISFCN (asym->n_type) && asym->n_numaux > 0 && asym->n_scnum > 0) + ++xcoff_symbol_count; + + i += asym->n_numaux; + } + + xcoff_symbol_size = xcoff_symbol_count * sizeof (struct xcoff_symbol); + xcoff_symbols = ((struct xcoff_symbol *) + backtrace_alloc (state, xcoff_symbol_size, error_callback, + data)); + if (xcoff_symbols == NULL) + return 0; + + j = 0; + for (i = 0; i < nsyms; ++i) + { + const b_xcoff_syment *asym = &syms[i]; + if ((asym->n_sclass == C_EXT || asym->n_sclass == C_HIDEXT + || asym->n_sclass == C_WEAKEXT) + && ISFCN (asym->n_type) && asym->n_numaux > 0 && asym->n_scnum > 0) + { + const b_xcoff_auxent *aux = (const b_xcoff_auxent *) (asym + 1); + xcoff_symbols[j].name = xcoff_symname (asym, strtab, strtab_size); + xcoff_symbols[j].address = base_address + asym->n_value + - sects[asym->n_scnum - 1].s_paddr; + /* x_fsize will be 0 if there is no debug information. */ + xcoff_symbols[j].size = aux->x_fcn.x_fsize; + ++j; + } + + i += asym->n_numaux; + } + + backtrace_qsort (xcoff_symbols, xcoff_symbol_count, + sizeof (struct xcoff_symbol), xcoff_symbol_compare); + + sdata->next = NULL; + sdata->symbols = xcoff_symbols; + sdata->count = xcoff_symbol_count; + + return 1; +} + +/* Compare struct xcoff_line for qsort. */ + +static int +xcoff_line_compare (const void *v1, const void *v2) +{ + const struct xcoff_line *ln1 = (const struct xcoff_line *) v1; + const struct xcoff_line *ln2 = (const struct xcoff_line *) v2; + + if (ln1->pc < ln2->pc) + return -1; + else if (ln1->pc > ln2->pc) + return 1; + else + return 0; +} + +/* Find a PC in a line vector. We always allocate an extra entry at + the end of the lines vector, so that this routine can safely look + at the next entry. */ + +static int +xcoff_line_search (const void *vkey, const void *ventry) +{ + const uintptr_t *key = (const uintptr_t *) vkey; + const struct xcoff_line *entry = (const struct xcoff_line *) ventry; + uintptr_t pc; + + pc = *key; + if (pc < entry->pc) + return -1; + else if ((entry + 1)->pc == (uintptr_t) -1 || pc >= (entry + 1)->pc) + return 1; + else + return 0; +} + +/* Look for a PC in the line vector for one module. On success, + call CALLBACK and return whatever it returns. On error, call + ERROR_CALLBACK and return 0. Sets *FOUND to 1 if the PC is found, + 0 if not. */ + +static int +xcoff_lookup_pc (struct backtrace_state *state ATTRIBUTE_UNUSED, + struct xcoff_fileline_data *fdata, uintptr_t pc, + backtrace_full_callback callback, + backtrace_error_callback error_callback ATTRIBUTE_UNUSED, + void *data, int *found) +{ + const struct xcoff_line *ln; + const char *function; + + *found = 1; + + ln = (struct xcoff_line *) bsearch (&pc, fdata->vec.vec.base, + fdata->vec.count, + sizeof (struct xcoff_line), + xcoff_line_search); + if (ln == NULL) + { + *found = 0; + return 0; + } + + function = ln->function; + /* AIX prepends a '.' to function entry points, remove it. */ + if (*function == '.') + ++function; + return callback (data, pc, ln->filename, ln->lineno, function); +} + +/* Return the file/line information for a PC using the XCOFF lineno + mapping we built earlier. */ + +static int +xcoff_fileline (struct backtrace_state *state, uintptr_t pc, + backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data) + +{ + struct xcoff_fileline_data *fdata; + int found; + int ret; + + if (!state->threaded) + { + for (fdata = (struct xcoff_fileline_data *) state->fileline_data; + fdata != NULL; + fdata = fdata->next) + { + ret = xcoff_lookup_pc (state, fdata, pc, callback, error_callback, + data, &found); + if (ret != 0 || found) + return ret; + } + } + else + { + struct xcoff_fileline_data **pp; + + pp = (struct xcoff_fileline_data **) (void *) &state->fileline_data; + while (1) + { + fdata = backtrace_atomic_load_pointer (pp); + if (fdata == NULL) + break; + + ret = xcoff_lookup_pc (state, fdata, pc, callback, error_callback, + data, &found); + if (ret != 0 || found) + return ret; + + pp = &fdata->next; + } + } + + /* FIXME: See if any libraries have been dlopen'ed. */ + + return callback (data, pc, NULL, 0, NULL); +} + +/* Add a new mapping to the vector of line mappings that we are + building. Returns 1 on success, 0 on failure. */ + +static int +xcoff_add_line (struct backtrace_state *state, uintptr_t pc, + const char *filename, const char *function, uint32_t lnno, + backtrace_error_callback error_callback, void *data, + struct xcoff_line_vector *vec) +{ + struct xcoff_line *ln; + + ln = ((struct xcoff_line *) + backtrace_vector_grow (state, sizeof (struct xcoff_line), + error_callback, data, &vec->vec)); + if (ln == NULL) + return 0; + + ln->pc = pc; + ln->filename = filename; + ln->function = function; + ln->lineno = lnno; + + ++vec->count; + + return 1; +} + +/* Add the line number entries for a function to the line vector. */ + +static int +xcoff_process_linenos (struct backtrace_state *state, uintptr_t base_address, + const b_xcoff_syment *fsym, const char *filename, + const b_xcoff_scnhdr *sects, + const unsigned char *strtab, size_t strtab_size, + uint32_t fcn_lnno, struct xcoff_incl_vector *vec, + struct xcoff_line_vector *lvec, + const unsigned char *linenos, size_t linenos_size, + uintptr_t lnnoptr0, + backtrace_error_callback error_callback, void *data) +{ + const b_xcoff_auxent *aux; + const b_xcoff_lineno *lineno; + const unsigned char *lineptr; + const char *function; + struct xcoff_incl *incl = NULL; + uintptr_t lnnoptr; + uintptr_t pc; + uint32_t lnno; + int begincl; + size_t i; + + aux = (const b_xcoff_auxent *) (fsym + 1); + lnnoptr = aux->x_fcn.x_lnnoptr; + + if (lnnoptr < lnnoptr0 || lnnoptr + LINESZ > lnnoptr0 + linenos_size) + return 0; + + function = xcoff_symname (fsym, strtab, strtab_size); + if (function == NULL) + return 0; + + /* Skip first entry that points to symtab. */ + + lnnoptr += LINESZ; + + lineptr = linenos + (lnnoptr - lnnoptr0); + + begincl = -1; + while (lineptr + LINESZ <= linenos + linenos_size) + { + lineno = (const b_xcoff_lineno *) lineptr; + + lnno = lineno->l_lnno; + if (lnno == 0) + break; + + /* If part of a function other than the beginning comes from an + include file, the line numbers are absolute, rather than + relative to the beginning of the function. */ + for (i = 0; i < vec->count; ++i) + { + incl = (struct xcoff_incl *) vec->vec.base + i; + if (incl->begin <= lnnoptr && lnnoptr <= incl->end) + break; + } + if (begincl == -1) + begincl = (i < vec->count); + if (i < vec->count) + { + filename = incl->filename; + if (begincl == 1) + lnno += fcn_lnno - 1; + } + else + lnno += fcn_lnno - 1; + + pc = base_address + lineno->l_addr.l_paddr + - sects[fsym->n_scnum - 1].s_paddr; + xcoff_add_line (state, pc, filename, function, lnno, error_callback, + data, lvec); + + lnnoptr += LINESZ; + lineptr += LINESZ; + } + + return 1; +} + +/* Initialize the line vector info for xcoff_fileline. */ + +static int +xcoff_initialize_fileline (struct backtrace_state *state, + uintptr_t base_address, + const b_xcoff_scnhdr *sects, + const b_xcoff_syment *syms, size_t nsyms, + const unsigned char *strtab, size_t strtab_size, + const unsigned char *linenos, size_t linenos_size, + uint64_t lnnoptr0, + backtrace_error_callback error_callback, void *data) +{ + struct xcoff_fileline_data *fdata; + struct xcoff_incl_vector vec; + struct xcoff_line *ln; + const b_xcoff_syment *fsym; + const b_xcoff_auxent *aux; + const char *filename; + const char *name; + struct xcoff_incl *incl; + uintptr_t begin, end; + uintptr_t lnno; + size_t i; + + fdata = ((struct xcoff_fileline_data *) + backtrace_alloc (state, sizeof (struct xcoff_fileline_data), + error_callback, data)); + if (fdata == NULL) + return 0; + + memset (fdata, 0, sizeof *fdata); + memset (&vec, 0, sizeof vec); + + /* Process include files first. */ + + begin = 0; + for (i = 0; i < nsyms; ++i) + { + const b_xcoff_syment *asym = &syms[i]; + + switch (asym->n_sclass) + { + case C_BINCL: + begin = asym->n_value; + break; + + case C_EINCL: + if (begin == 0) + break; + end = asym->n_value; + incl = ((struct xcoff_incl *) + backtrace_vector_grow (state, sizeof (struct xcoff_incl), + error_callback, data, &vec.vec)); + if (incl != NULL) + { + incl->filename = xcoff_symname (asym, strtab, strtab_size); + incl->begin = begin; + incl->end = end; + ++vec.count; + } + begin = 0; + break; + } + + i += asym->n_numaux; + } + + filename = NULL; + fsym = NULL; + for (i = 0; i < nsyms; ++i) + { + const b_xcoff_syment *asym = &syms[i]; + + switch (asym->n_sclass) + { + case C_FILE: + filename = xcoff_symname (asym, strtab, strtab_size); + if (filename == NULL) + break; + + /* If the file auxiliary entry is not used, the symbol name is + the name of the source file. If the file auxiliary entry is + used, then the symbol name should be .file, and the first + file auxiliary entry (by convention) contains the source + file name. */ + + if (asym->n_numaux > 0 && !strcmp (filename, ".file")) + { + aux = (const b_xcoff_auxent *) (asym + 1); + if (aux->x_file._x.x_zeroes != 0) + { + /* Make a copy as we will release the symtab view. */ + char name[FILNMLEN+1]; + strncpy (name, aux->x_file.x_fname, FILNMLEN); + name[FILNMLEN] = '\0'; + filename = strdup (name); + } + else if (aux->x_file._x.x_offset < strtab_size) + filename = (const char *) strtab + aux->x_file._x.x_offset; + else + filename = NULL; + } + break; + + case C_EXT: + case C_HIDEXT: + case C_WEAKEXT: + fsym = NULL; + if (!ISFCN (asym->n_type) || asym->n_numaux == 0) + break; + if (filename == NULL) + break; + fsym = asym; + break; + + case C_FCN: + if (asym->n_numaux == 0) + break; + if (fsym == NULL) + break; + name = xcoff_symname (asym, strtab, strtab_size); + if (name == NULL) + break; + aux = (const b_xcoff_auxent *) (asym + 1); +#if BACKTRACE_XCOFF_SIZE == 32 + lnno = (uint32_t) aux->x_block.x_lnnohi << 16 + | aux->x_block.x_lnno; +#else + lnno = aux->x_block.x_lnno; +#endif + if (!strcmp (name, ".bf")) + { + xcoff_process_linenos (state, base_address, fsym, filename, + sects, strtab, strtab_size, lnno, &vec, + &fdata->vec, linenos, linenos_size, + lnnoptr0, error_callback, data); + } + else if (!strcmp (name, ".ef")) + { + fsym = NULL; + } + break; + } + + i += asym->n_numaux; + } + + /* Allocate one extra entry at the end. */ + ln = ((struct xcoff_line *) + backtrace_vector_grow (state, sizeof (struct xcoff_line), + error_callback, data, &fdata->vec.vec)); + if (ln == NULL) + goto fail; + ln->pc = (uintptr_t) -1; + ln->filename = NULL; + ln->function = NULL; + ln->lineno = 0; + + if (!backtrace_vector_release (state, &fdata->vec.vec, error_callback, data)) + goto fail; + + backtrace_qsort (fdata->vec.vec.base, fdata->vec.count, + sizeof (struct xcoff_line), xcoff_line_compare); + + if (!state->threaded) + { + struct xcoff_fileline_data **pp; + + for (pp = (struct xcoff_fileline_data **) (void *) &state->fileline_data; + *pp != NULL; + pp = &(*pp)->next) + ; + *pp = fdata; + } + else + { + while (1) + { + struct xcoff_fileline_data **pp; + + pp = (struct xcoff_fileline_data **) (void *) &state->fileline_data; + + while (1) + { + struct xcoff_fileline_data *p; + + p = backtrace_atomic_load_pointer (pp); + + if (p == NULL) + break; + + pp = &p->next; + } + + if (__sync_bool_compare_and_swap (pp, NULL, fdata)) + break; + } + } + + return 1; + +fail: + return 0; +} + +/* Add the backtrace data for one XCOFF file. Returns 1 on success, + 0 on failure (in both cases descriptor is closed). */ + +static int +xcoff_add (struct backtrace_state *state, int descriptor, off_t offset, + uintptr_t base_address, backtrace_error_callback error_callback, + void *data, fileline *fileline_fn, int *found_sym, int exe) +{ + struct backtrace_view fhdr_view; + struct backtrace_view sects_view; + struct backtrace_view linenos_view; + struct backtrace_view syms_view; + struct backtrace_view str_view; + b_xcoff_filhdr fhdr; + const b_xcoff_scnhdr *sects; + const b_xcoff_scnhdr *stext; + uint64_t lnnoptr; + uint32_t nlnno; + off_t str_off; + size_t sects_size; + size_t syms_size; + int32_t str_size; + int sects_view_valid; + int linenos_view_valid; + int syms_view_valid; + int str_view_valid; + int magic_ok; + int i; + + *found_sym = 0; + + sects_view_valid = 0; + linenos_view_valid = 0; + syms_view_valid = 0; + str_view_valid = 0; + + /* Map the XCOFF file header. */ + if (!backtrace_get_view (state, descriptor, offset, sizeof (b_xcoff_filhdr), + error_callback, data, &fhdr_view)) + goto fail; + + memcpy (&fhdr, fhdr_view.data, sizeof fhdr); + magic_ok = (fhdr.f_magic == XCOFF_MAGIC); + + backtrace_release_view (state, &fhdr_view, error_callback, data); + + if (!magic_ok) + { + if (exe) + error_callback (data, "executable file is not XCOFF", 0); + goto fail; + } + + /* Verify object is of expected type. */ + if ((exe && (fhdr.f_flags & F_SHROBJ)) + || (!exe && !(fhdr.f_flags & F_SHROBJ))) + goto fail; + + /* Read the section headers. */ + + sects_size = fhdr.f_nscns * sizeof (b_xcoff_scnhdr); + + if (!backtrace_get_view (state, descriptor, + offset + sizeof (fhdr) + fhdr.f_opthdr, + sects_size, error_callback, data, §s_view)) + goto fail; + sects_view_valid = 1; + sects = (const b_xcoff_scnhdr *) sects_view.data; + + /* FIXME: assumes only one .text section. */ + for (i = 0; i < fhdr.f_nscns; ++i) + if ((sects[i].s_flags & 0xffff) == STYP_TEXT) + break; + if (i == fhdr.f_nscns) + goto fail; + + stext = §s[i]; + + /* AIX ldinfo_textorg includes the XCOFF headers. */ + base_address = (exe ? XCOFF_AIX_TEXTBASE : base_address) + stext->s_scnptr; + + lnnoptr = stext->s_lnnoptr; + nlnno = stext->s_nlnno; + +#if BACKTRACE_XCOFF_SIZE == 32 + if (nlnno == _OVERFLOW_MARKER) + { + int sntext = i + 1; + /* Find the matching .ovrflo section. */ + for (i = 0; i < fhdr.f_nscns; ++i) + { + if (((sects[i].s_flags & 0xffff) == STYP_OVRFLO) + && sects[i].s_nlnno == sntext) + { + nlnno = sects[i].s_vaddr; + break; + } + } + } +#endif + + /* Read the symbol table and the string table. */ + + if (fhdr.f_symptr != 0) + { + struct xcoff_syminfo_data *sdata; + + /* Symbol table is followed by the string table. The string table + starts with its length (on 4 bytes). + Map the symbol table and the length of the string table. */ + syms_size = fhdr.f_nsyms * sizeof (b_xcoff_syment); + + if (!backtrace_get_view (state, descriptor, offset + fhdr.f_symptr, + syms_size + 4, error_callback, data, + &syms_view)) + goto fail; + syms_view_valid = 1; + + memcpy (&str_size, syms_view.data + syms_size, 4); + + str_off = fhdr.f_symptr + syms_size; + + if (str_size > 4) + { + /* Map string table (including the length word). */ + + if (!backtrace_get_view (state, descriptor, offset + str_off, + str_size, error_callback, data, &str_view)) + goto fail; + str_view_valid = 1; + } + + sdata = ((struct xcoff_syminfo_data *) + backtrace_alloc (state, sizeof *sdata, error_callback, data)); + if (sdata == NULL) + goto fail; + + if (!xcoff_initialize_syminfo (state, base_address, sects, + syms_view.data, fhdr.f_nsyms, + str_view.data, str_size, + error_callback, data, sdata)) + { + backtrace_free (state, sdata, sizeof *sdata, error_callback, data); + goto fail; + } + + *found_sym = 1; + + xcoff_add_syminfo_data (state, sdata); + } + + /* Read the line number entries. */ + + if (fhdr.f_symptr != 0 && lnnoptr != 0) + { + size_t linenos_size = (size_t) nlnno * LINESZ; + + if (!backtrace_get_view (state, descriptor, offset + lnnoptr, + linenos_size, + error_callback, data, &linenos_view)) + goto fail; + linenos_view_valid = 1; + + if (xcoff_initialize_fileline (state, base_address, sects, + syms_view.data, fhdr.f_nsyms, + str_view.data, str_size, + linenos_view.data, linenos_size, + lnnoptr, error_callback, data)) + *fileline_fn = xcoff_fileline; + + backtrace_release_view (state, &linenos_view, error_callback, data); + linenos_view_valid = 0; + } + + backtrace_release_view (state, §s_view, error_callback, data); + sects_view_valid = 0; + if (syms_view_valid) + backtrace_release_view (state, &syms_view, error_callback, data); + syms_view_valid = 0; + + /* We've read all we need from the executable. */ + if (!backtrace_close (descriptor, error_callback, data)) + goto fail; + descriptor = -1; + + return 1; + + fail: + if (sects_view_valid) + backtrace_release_view (state, §s_view, error_callback, data); + if (str_view_valid) + backtrace_release_view (state, &str_view, error_callback, data); + if (syms_view_valid) + backtrace_release_view (state, &syms_view, error_callback, data); + if (linenos_view_valid) + backtrace_release_view (state, &linenos_view, error_callback, data); + if (descriptor != -1 && offset == 0) + backtrace_close (descriptor, error_callback, data); + return 0; +} + +#ifdef HAVE_LOADQUERY + +/* Read an integer value in human-readable format from an AIX + big archive fixed-length or member header. */ + +static int +xcoff_parse_decimal (const char *buf, size_t size, off_t *off) +{ + char str[32]; + char *end; + + if (size >= sizeof str) + return 0; + memcpy (str, buf, size); + str[size] = '\0'; + *off = strtol (str, &end, 10); + if (*end != '\0' && *end != ' ') + return 0; + + return 1; +} + +/* Add the backtrace data for a member of an AIX big archive. + Returns 1 on success, 0 on failure. */ + +static int +xcoff_armem_add (struct backtrace_state *state, int descriptor, + uintptr_t base_address, const char *member, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn, int *found_sym) +{ + struct backtrace_view view; + b_ar_fl_hdr fl_hdr; + const b_ar_hdr *ar_hdr; + off_t off; + off_t len; + int memlen; + + *found_sym = 0; + + /* Map archive fixed-length header. */ + + if (!backtrace_get_view (state, descriptor, 0, sizeof (b_ar_fl_hdr), + error_callback, data, &view)) + goto fail; + + memcpy (&fl_hdr, view.data, sizeof (b_ar_fl_hdr)); + + backtrace_release_view (state, &view, error_callback, data); + + if (memcmp (fl_hdr.fl_magic, AIAMAGBIG, 8) != 0) + goto fail; + + memlen = strlen (member); + + /* Read offset of first archive member. */ + if (!xcoff_parse_decimal (fl_hdr.fl_fstmoff, sizeof fl_hdr.fl_fstmoff, &off)) + goto fail; + while (off != 0) + { + /* Map archive member header and member name. */ + + if (!backtrace_get_view (state, descriptor, off, + sizeof (b_ar_hdr) + memlen, + error_callback, data, &view)) + break; + + ar_hdr = (const b_ar_hdr *) view.data; + + /* Read archive member name length. */ + if (!xcoff_parse_decimal (ar_hdr->ar_namlen, sizeof ar_hdr->ar_namlen, + &len)) + { + backtrace_release_view (state, &view, error_callback, data); + break; + } + if (len == memlen && !memcmp (ar_hdr->ar_name, member, memlen)) + { + off = (off + sizeof (b_ar_hdr) + memlen + 1) & ~1; + + /* The archive can contain several members with the same name + (e.g. 32-bit and 64-bit), so continue if not ok. */ + + if (xcoff_add (state, descriptor, off, base_address, error_callback, + data, fileline_fn, found_sym, 0)) + { + backtrace_release_view (state, &view, error_callback, data); + return 1; + } + } + + /* Read offset of next archive member. */ + if (!xcoff_parse_decimal (ar_hdr->ar_nxtmem, sizeof ar_hdr->ar_nxtmem, + &off)) + { + backtrace_release_view (state, &view, error_callback, data); + break; + } + backtrace_release_view (state, &view, error_callback, data); + } + + fail: + /* No matching member found. */ + backtrace_close (descriptor, error_callback, data); + return 0; +} + +/* Add the backtrace data for dynamically loaded libraries. */ + +static void +xcoff_add_shared_libs (struct backtrace_state *state, + backtrace_error_callback error_callback, + void *data, fileline *fileline_fn, int *found_sym) +{ + const struct ld_info *ldinfo; + void *buf; + unsigned int buflen; + const char *member; + int descriptor; + int does_not_exist; + int lib_found_sym; + int ret; + + /* Retrieve the list of loaded libraries. */ + + buf = NULL; + buflen = 512; + do + { + buf = realloc (buf, buflen); + if (buf == NULL) + { + ret = -1; + break; + } + ret = loadquery (L_GETINFO, buf, buflen); + if (ret == 0) + break; + buflen *= 2; + } + while (ret == -1 && errno == ENOMEM); + if (ret != 0) + { + free (buf); + return; + } + + ldinfo = (const struct ld_info *) buf; + while ((const char *) ldinfo < (const char *) buf + buflen) + { + if (*ldinfo->ldinfo_filename != '/') + goto next; + + descriptor = backtrace_open (ldinfo->ldinfo_filename, error_callback, + data, &does_not_exist); + if (descriptor < 0) + goto next; + + /* Check if it is an archive (member name not empty). */ + + member = ldinfo->ldinfo_filename + strlen (ldinfo->ldinfo_filename) + 1; + if (*member) + { + xcoff_armem_add (state, descriptor, + (uintptr_t) ldinfo->ldinfo_textorg, member, + error_callback, data, fileline_fn, &lib_found_sym); + } + else + { + xcoff_add (state, descriptor, 0, (uintptr_t) ldinfo->ldinfo_textorg, + error_callback, data, fileline_fn, &lib_found_sym, 0); + } + if (lib_found_sym) + *found_sym = 1; + + next: + if (ldinfo->ldinfo_next == 0) + break; + ldinfo = (const struct ld_info *) ((const char *) ldinfo + + ldinfo->ldinfo_next); + } + + free (buf); +} +#endif /* HAVE_LOADQUERY */ + +/* Initialize the backtrace data we need from an XCOFF executable. + Returns 1 on success, 0 on failure. */ + +int +backtrace_initialize (struct backtrace_state *state, int descriptor, + backtrace_error_callback error_callback, + void *data, fileline *fileline_fn) +{ + int ret; + int found_sym; + fileline xcoff_fileline_fn = xcoff_nodebug; + + ret = xcoff_add (state, descriptor, 0, 0, error_callback, data, + &xcoff_fileline_fn, &found_sym, 1); + if (!ret) + return 0; + +#ifdef HAVE_LOADQUERY + xcoff_add_shared_libs (state, error_callback, data, &xcoff_fileline_fn, + &found_sym); +#endif + + if (!state->threaded) + { + if (found_sym) + state->syminfo_fn = xcoff_syminfo; + else if (state->syminfo_fn == NULL) + state->syminfo_fn = xcoff_nosyms; + } + else + { + if (found_sym) + backtrace_atomic_store_pointer (&state->syminfo_fn, xcoff_syminfo); + else + __sync_bool_compare_and_swap (&state->syminfo_fn, NULL, xcoff_nosyms); + } + + if (!state->threaded) + { + if (state->fileline_fn == NULL || state->fileline_fn == xcoff_nodebug) + *fileline_fn = xcoff_fileline_fn; + } + else + { + fileline current_fn; + + current_fn = backtrace_atomic_load_pointer (&state->fileline_fn); + if (current_fn == NULL || current_fn == xcoff_nodebug) + *fileline_fn = xcoff_fileline_fn; + } + + return 1; +} -- 2.7.4 From d840e4e9682684c351c25a1d7dc79111b12e18bb Mon Sep 17 00:00:00 2001 From: Denis Khalikov Date: Thu, 7 Jun 2018 14:24:16 +0300 Subject: [PATCH 05/16] PR sanitizer/77631 Support for external debug info. * elf.c: Include , , . (S_ISLNK): Define if not defined. (xstrnlen): Define if strnlen is not available. (b_elf_note): Define type. (NT_GNU_BUILD_ID): Define macro. (elf_crc32, elf_crc32_file): New static functions. (elf_is_symlink, elf_readlink): New static functions. (elf_open_debugfile_by_buildid): New static function. (elf_try_debugfile): New static function. (elf_find_debugfile_by_debuglink): New static function. (elf_open_debugfile_by_debuglink): New static function. (elf_add): Add filename and debuginfo parameters. Adjust all callers. Look for external debug info notes, and try to fetch debug info from external file. (struct phdr_data): Add exe_filename field. (phdr_callback): Pass filename to elf_add. (backtrace_initialize): Add filename parameter. * internal.h (backtrace_initialize): Add filename parameter. * fileline.c (fileline_initialize): Pass filename to backtrace_initialize. * pecoff.c (fileline_initialize): Add unused filename parameter. * unknown.c (fileline_initialize): Likewise. * xcoff.c (fileline_initialize): Likewise. * configure.ac: Check for objcopy --add-gnu-debuglink. * Makefile.am (dtest): New test target. * configure, Makefile.in: Rebuild. * elf.c (backtrace_initialize): Set pd.exe_filename. * configure.ac: Check for lstat and readlink. * elf.c (lstat, readlink): Provide dummy versions if real versions are not available. * configure, config.h.in: Rebuild. Change-Id: Ia723266f2d1a9d075202c0b6904e62fa18b75123 --- libbacktrace/ChangeLog | 45 ++++ libbacktrace/Makefile.am | 10 + libbacktrace/Makefile.in | 8 +- libbacktrace/config.h.in | 6 + libbacktrace/configure | 91 ++++++- libbacktrace/configure.ac | 15 ++ libbacktrace/elf.c | 598 +++++++++++++++++++++++++++++++++++++++++++++- libbacktrace/fileline.c | 6 +- libbacktrace/internal.h | 1 + libbacktrace/pecoff.c | 3 +- libbacktrace/unknown.c | 1 + libbacktrace/xcoff.c | 3 +- 12 files changed, 768 insertions(+), 19 deletions(-) diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog index e2285fd..1c72eb0 100644 --- a/libbacktrace/ChangeLog +++ b/libbacktrace/ChangeLog @@ -1,3 +1,48 @@ +2017-09-22 Ian Lance Taylor + + PR sanitizer/77631 + * configure.ac: Check for lstat and readlink. + * elf.c (lstat, readlink): Provide dummy versions if real versions + are not available. + * configure, config.h.in: Rebuild. + +2017-09-21 Ian Lance Taylor + + PR go/82284 + * elf.c (backtrace_initialize): Set pd.exe_filename. + +2017-09-20 Ian Lance Taylor + Denis Khalikov + + PR sanitizer/77631 + Support for external debug info. + * elf.c: Include , , . + (S_ISLNK): Define if not defined. + (xstrnlen): Define if strnlen is not available. + (b_elf_note): Define type. + (NT_GNU_BUILD_ID): Define macro. + (elf_crc32, elf_crc32_file): New static functions. + (elf_is_symlink, elf_readlink): New static functions. + (elf_open_debugfile_by_buildid): New static function. + (elf_try_debugfile): New static function. + (elf_find_debugfile_by_debuglink): New static function. + (elf_open_debugfile_by_debuglink): New static function. + (elf_add): Add filename and debuginfo parameters. Adjust all + callers. Look for external debug info notes, and try to fetch + debug info from external file. + (struct phdr_data): Add exe_filename field. + (phdr_callback): Pass filename to elf_add. + (backtrace_initialize): Add filename parameter. + * internal.h (backtrace_initialize): Add filename parameter. + * fileline.c (fileline_initialize): Pass filename to + backtrace_initialize. + * pecoff.c (fileline_initialize): Add unused filename parameter. + * unknown.c (fileline_initialize): Likewise. + * xcoff.c (fileline_initialize): Likewise. + * configure.ac: Check for objcopy --add-gnu-debuglink. + * Makefile.am (dtest): New test target. + * configure, Makefile.in: Rebuild. + 2017-09-12 Steve Ellcey PR other/81096 diff --git a/libbacktrace/Makefile.am b/libbacktrace/Makefile.am index ec7dde0..65635bf 100644 --- a/libbacktrace/Makefile.am +++ b/libbacktrace/Makefile.am @@ -122,6 +122,16 @@ ttest_LDADD = libbacktrace.la endif HAVE_PTHREAD +if HAVE_OBJCOPY_DEBUGLINK + +TESTS += dtest + +dtest: btest + $(OBJCOPY) --only-keep-debug btest btest.debug + $(OBJCOPY) --strip-debug --add-gnu-debuglink=btest.debug btest dtest + +endif HAVE_OBJCOPY_DEBUGLINK + endif NATIVE # We can't use automake's automatic dependency tracking, because it diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in index be5b4b6..a659ec0 100644 --- a/libbacktrace/Makefile.in +++ b/libbacktrace/Makefile.in @@ -86,6 +86,7 @@ target_triplet = @target@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) @NATIVE_TRUE@am__append_1 = btest stest edtest @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__append_2 = ttest +@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_3 = dtest subdir = . DIST_COMMON = README ChangeLog $(srcdir)/Makefile.in \ $(srcdir)/Makefile.am $(top_srcdir)/configure \ @@ -217,6 +218,7 @@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ +OBJCOPY = @OBJCOPY@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ @@ -343,7 +345,7 @@ libbacktrace_la_LIBADD = \ $(ALLOC_FILE) libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD) -TESTS = $(check_PROGRAMS) +TESTS = $(check_PROGRAMS) $(am__append_3) @NATIVE_TRUE@btest_SOURCES = btest.c testlib.c @NATIVE_TRUE@btest_CFLAGS = $(AM_CFLAGS) -g -O @NATIVE_TRUE@btest_LDADD = libbacktrace.la @@ -799,6 +801,10 @@ uninstall-am: @NATIVE_TRUE@ cat $(srcdir)/edtest2.c > tmp-edtest2_build.c @NATIVE_TRUE@ $(SHELL) $(srcdir)/../move-if-change tmp-edtest2_build.c edtest2_build.c @NATIVE_TRUE@ echo timestamp > $@ + +@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@dtest: btest +@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@ $(OBJCOPY) --only-keep-debug btest btest.debug +@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@ $(OBJCOPY) --strip-debug --add-gnu-debuglink=btest.debug btest dtest alloc.lo: config.h backtrace.h internal.h backtrace.lo: config.h backtrace.h internal.h btest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in index 9fc7715..1c38e8e 100644 --- a/libbacktrace/config.h.in +++ b/libbacktrace/config.h.in @@ -37,9 +37,15 @@ /* Define if AIX loadquery is available. */ #undef HAVE_LOADQUERY +/* Define to 1 if you have the `lstat' function. */ +#undef HAVE_LSTAT + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H +/* Define to 1 if you have the `readlink' function. */ +#undef HAVE_READLINK + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H diff --git a/libbacktrace/configure b/libbacktrace/configure index b7b88e2..785fcbe 100755 --- a/libbacktrace/configure +++ b/libbacktrace/configure @@ -604,6 +604,9 @@ LTLIBOBJS LIBOBJS NATIVE_FALSE NATIVE_TRUE +HAVE_OBJCOPY_DEBUGLINK_FALSE +HAVE_OBJCOPY_DEBUGLINK_TRUE +OBJCOPY HAVE_PTHREAD_FALSE HAVE_PTHREAD_TRUE PTHREAD_CFLAGS @@ -746,7 +749,8 @@ CFLAGS LDFLAGS LIBS CPPFLAGS -CPP' +CPP +OBJCOPY' # Initialize some variables set by options. @@ -1396,6 +1400,7 @@ Some influential environment variables: CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor + OBJCOPY location of objcopy Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -11136,7 +11141,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11139 "configure" +#line 11144 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11242,7 +11247,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11245 "configure" +#line 11250 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -12703,6 +12708,19 @@ cat >>confdefs.h <<_ACEOF #define HAVE_DECL_STRNLEN $ac_have_decl _ACEOF +for ac_func in lstat readlink +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + # Check for getexecname function. if test -n "${with_target_subdir}"; then @@ -12761,6 +12779,69 @@ else fi + +# Extract the first word of "objcopy", so it can be a program name with args. +set dummy objcopy; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_OBJCOPY+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJCOPY"; then + ac_cv_prog_OBJCOPY="$OBJCOPY" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_OBJCOPY="objcopy" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJCOPY=$ac_cv_prog_OBJCOPY +if test -n "$OBJCOPY"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJCOPY" >&5 +$as_echo "$OBJCOPY" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether objcopy supports debuglink" >&5 +$as_echo_n "checking whether objcopy supports debuglink... " >&6; } +if test "${libbacktrace_cv_objcopy_debuglink+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "${with_target_subdir}"; then + libbacktrace_cv_objcopy_debuglink=no +elif ${OBJCOPY} --add-gnu-debuglink=x /bin/ls /tmp/ls$$; then + rm -f /tmp/ls$$ + libbacktrace_cv_objcopy_debuglink=yes +else + libbacktrace_cv_objcopy_debuglink=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libbacktrace_cv_objcopy_debuglink" >&5 +$as_echo "$libbacktrace_cv_objcopy_debuglink" >&6; } + if test "$libbacktrace_cv_objcopy_debuglink" = yes; then + HAVE_OBJCOPY_DEBUGLINK_TRUE= + HAVE_OBJCOPY_DEBUGLINK_FALSE='#' +else + HAVE_OBJCOPY_DEBUGLINK_TRUE='#' + HAVE_OBJCOPY_DEBUGLINK_FALSE= +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether tests can run" >&5 $as_echo_n "checking whether tests can run... " >&6; } if test "${libbacktrace_cv_sys_native+set}" = set; then : @@ -12927,6 +13008,10 @@ if test -z "${HAVE_PTHREAD_TRUE}" && test -z "${HAVE_PTHREAD_FALSE}"; then as_fn_error "conditional \"HAVE_PTHREAD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${HAVE_OBJCOPY_DEBUGLINK_TRUE}" && test -z "${HAVE_OBJCOPY_DEBUGLINK_FALSE}"; then + as_fn_error "conditional \"HAVE_OBJCOPY_DEBUGLINK\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${NATIVE_TRUE}" && test -z "${NATIVE_FALSE}"; then as_fn_error "conditional \"NATIVE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac index 03e33c5..34d1591 100644 --- a/libbacktrace/configure.ac +++ b/libbacktrace/configure.ac @@ -373,6 +373,7 @@ if test "$have_fcntl" = "yes"; then fi AC_CHECK_DECLS(strnlen) +AC_CHECK_FUNCS(lstat readlink) # Check for getexecname function. if test -n "${with_target_subdir}"; then @@ -404,6 +405,20 @@ AC_SUBST(PTHREAD_CFLAGS) AM_CONDITIONAL(HAVE_PTHREAD, test "$libgo_cv_lib_pthread" = yes) +AC_ARG_VAR(OBJCOPY, [location of objcopy]) +AC_CHECK_PROG(OBJCOPY, objcopy, objcopy,) +AC_CACHE_CHECK([whether objcopy supports debuglink], +[libbacktrace_cv_objcopy_debuglink], +[if test -n "${with_target_subdir}"; then + libbacktrace_cv_objcopy_debuglink=no +elif ${OBJCOPY} --add-gnu-debuglink=x /bin/ls /tmp/ls$$; then + rm -f /tmp/ls$$ + libbacktrace_cv_objcopy_debuglink=yes +else + libbacktrace_cv_objcopy_debuglink=no +fi]) +AM_CONDITIONAL(HAVE_OBJCOPY_DEBUGLINK, test "$libbacktrace_cv_objcopy_debuglink" = yes) + AC_CACHE_CHECK([whether tests can run], [libbacktrace_cv_sys_native], [AC_RUN_IFELSE([AC_LANG_PROGRAM([], [return 0;])], diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c index ed6f4e1..9452681 100644 --- a/libbacktrace/elf.c +++ b/libbacktrace/elf.c @@ -32,9 +32,12 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" +#include #include #include #include +#include +#include #ifdef HAVE_DL_ITERATE_PHDR #include @@ -43,6 +46,64 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "backtrace.h" #include "internal.h" +#ifndef S_ISLNK + #ifndef S_IFLNK + #define S_IFLNK 0120000 + #endif + #ifndef S_IFMT + #define S_IFMT 0170000 + #endif + #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif + +#if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN + +/* If strnlen is not declared, provide our own version. */ + +static size_t +xstrnlen (const char *s, size_t maxlen) +{ + size_t i; + + for (i = 0; i < maxlen; ++i) + if (s[i] == '\0') + break; + return i; +} + +#define strnlen xstrnlen + +#endif + +#ifndef HAVE_LSTAT + +/* Dummy version of lstat for systems that don't have it. */ + +static int +xlstat (const char *path ATTRIBUTE_UNUSED, struct stat *st ATTRIBUTE_UNUSED) +{ + return -1; +} + +#define lstat xlstat + +#endif + +#ifndef HAVE_READLINK + +/* Dummy version of readlink for systems that don't have it. */ + +static ssize_t +xreadlink (const char *path ATTRIBUTE_UNUSED, char *buf ATTRIBUTE_UNUSED, + size_t bufsz ATTRIBUTE_UNUSED) +{ + return -1; +} + +#define readlink xreadlink + +#endif + #ifndef HAVE_DL_ITERATE_PHDR /* Dummy version of dl_iterate_phdr for systems that don't have it. */ @@ -105,6 +166,7 @@ dl_iterate_phdr (int (*callback) (struct dl_phdr_info *, #undef SHT_DYNSYM #undef STT_OBJECT #undef STT_FUNC +#undef NT_GNU_BUILD_ID /* Basic types. */ @@ -224,6 +286,16 @@ typedef struct #define STT_OBJECT 1 #define STT_FUNC 2 +typedef struct +{ + uint32_t namesz; + uint32_t descsz; + uint32_t type; + char name[1]; +} b_elf_note; + +#define NT_GNU_BUILD_ID 3 + /* An index of ELF sections we care about. */ enum debug_section @@ -283,6 +355,102 @@ struct elf_syminfo_data size_t count; }; +/* Compute the CRC-32 of BUF/LEN. This uses the CRC used for + .gnu_debuglink files. */ + +static uint32_t +elf_crc32 (uint32_t crc, const unsigned char *buf, size_t len) +{ + static const uint32_t crc32_table[256] = + { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d + }; + const unsigned char *end; + + crc = ~crc; + for (end = buf + len; buf < end; ++ buf) + crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); + return ~crc; +} + +/* Return the CRC-32 of the entire file open at DESCRIPTOR. */ + +static uint32_t +elf_crc32_file (struct backtrace_state *state, int descriptor, + backtrace_error_callback error_callback, void *data) +{ + struct stat st; + struct backtrace_view file_view; + uint32_t ret; + + if (fstat (descriptor, &st) < 0) + { + error_callback (data, "fstat", errno); + return 0; + } + + if (!backtrace_get_view (state, descriptor, 0, st.st_size, error_callback, + data, &file_view)) + return 0; + + ret = elf_crc32 (0, (const unsigned char *) file_view.data, st.st_size); + + backtrace_release_view (state, &file_view, error_callback, data); + + return ret; +} + /* A dummy callback function used when we can't find any debug info. */ static int @@ -510,6 +678,293 @@ elf_syminfo (struct backtrace_state *state, uintptr_t addr, callback (data, addr, sym->name, sym->address, sym->size); } +/* Return whether FILENAME is a symlink. */ + +static int +elf_is_symlink (const char *filename) +{ + struct stat st; + + if (lstat (filename, &st) < 0) + return 0; + return S_ISLNK (st.st_mode); +} + +/* Return the results of reading the symlink FILENAME in a buffer + allocated by backtrace_alloc. Return the length of the buffer in + *LEN. */ + +static char * +elf_readlink (struct backtrace_state *state, const char *filename, + backtrace_error_callback error_callback, void *data, + size_t *plen) +{ + size_t len; + char *buf; + + len = 128; + while (1) + { + ssize_t rl; + + buf = backtrace_alloc (state, len, error_callback, data); + if (buf == NULL) + return NULL; + rl = readlink (filename, buf, len); + if (rl < 0) + { + backtrace_free (state, buf, len, error_callback, data); + return NULL; + } + if ((size_t) rl < len - 1) + { + buf[rl] = '\0'; + *plen = len; + return buf; + } + backtrace_free (state, buf, len, error_callback, data); + len *= 2; + } +} + +/* Open a separate debug info file, using the build ID to find it. + Returns an open file descriptor, or -1. + + The GDB manual says that the only place gdb looks for a debug file + when the build ID is known is in /usr/lib/debug/.build-id. */ + +static int +elf_open_debugfile_by_buildid (struct backtrace_state *state, + const char *buildid_data, size_t buildid_size, + backtrace_error_callback error_callback, + void *data) +{ + const char * const prefix = "/usr/lib/debug/.build-id/"; + const size_t prefix_len = strlen (prefix); + const char * const suffix = ".debug"; + const size_t suffix_len = strlen (suffix); + size_t len; + char *bd_filename; + char *t; + size_t i; + int ret; + int does_not_exist; + + len = prefix_len + buildid_size * 2 + suffix_len + 2; + bd_filename = backtrace_alloc (state, len, error_callback, data); + if (bd_filename == NULL) + return -1; + + t = bd_filename; + memcpy (t, prefix, prefix_len); + t += prefix_len; + for (i = 0; i < buildid_size; i++) + { + unsigned char b; + unsigned char nib; + + b = (unsigned char) buildid_data[i]; + nib = (b & 0xf0) >> 4; + *t++ = nib < 10 ? '0' + nib : 'a' + nib - 10; + nib = b & 0x0f; + *t++ = nib < 10 ? '0' + nib : 'a' + nib - 10; + if (i == 0) + *t++ = '/'; + } + memcpy (t, suffix, suffix_len); + t[suffix_len] = '\0'; + + ret = backtrace_open (bd_filename, error_callback, data, &does_not_exist); + + backtrace_free (state, bd_filename, len, error_callback, data); + + /* gdb checks that the debuginfo file has the same build ID note. + That seems kind of pointless to me--why would it have the right + name but not the right build ID?--so skipping the check. */ + + return ret; +} + +/* Try to open a file whose name is PREFIX (length PREFIX_LEN) + concatenated with PREFIX2 (length PREFIX2_LEN) concatenated with + DEBUGLINK_NAME. Returns an open file descriptor, or -1. */ + +static int +elf_try_debugfile (struct backtrace_state *state, const char *prefix, + size_t prefix_len, const char *prefix2, size_t prefix2_len, + const char *debuglink_name, + backtrace_error_callback error_callback, void *data) +{ + size_t debuglink_len; + size_t try_len; + char *try; + int does_not_exist; + int ret; + + debuglink_len = strlen (debuglink_name); + try_len = prefix_len + prefix2_len + debuglink_len + 1; + try = backtrace_alloc (state, try_len, error_callback, data); + if (try == NULL) + return -1; + + memcpy (try, prefix, prefix_len); + memcpy (try + prefix_len, prefix2, prefix2_len); + memcpy (try + prefix_len + prefix2_len, debuglink_name, debuglink_len); + try[prefix_len + prefix2_len + debuglink_len] = '\0'; + + ret = backtrace_open (try, error_callback, data, &does_not_exist); + + backtrace_free (state, try, try_len, error_callback, data); + + return ret; +} + +/* Find a separate debug info file, using the debuglink section data + to find it. Returns an open file descriptor, or -1. */ + +static int +elf_find_debugfile_by_debuglink (struct backtrace_state *state, + const char *filename, + const char *debuglink_name, + backtrace_error_callback error_callback, + void *data) +{ + int ret; + char *alc; + size_t alc_len; + const char *slash; + int ddescriptor; + const char *prefix; + size_t prefix_len; + + /* Resolve symlinks in FILENAME. Since FILENAME is fairly likely to + be /proc/self/exe, symlinks are common. We don't try to resolve + the whole path name, just the base name. */ + ret = -1; + alc = NULL; + alc_len = 0; + while (elf_is_symlink (filename)) + { + char *new_buf; + size_t new_len; + + new_buf = elf_readlink (state, filename, error_callback, data, &new_len); + if (new_buf == NULL) + break; + + if (new_buf[0] == '/') + filename = new_buf; + else + { + slash = strrchr (filename, '/'); + if (slash == NULL) + filename = new_buf; + else + { + size_t clen; + char *c; + + slash++; + clen = slash - filename + strlen (new_buf) + 1; + c = backtrace_alloc (state, clen, error_callback, data); + if (c == NULL) + goto done; + + memcpy (c, filename, slash - filename); + memcpy (c + (slash - filename), new_buf, strlen (new_buf)); + c[slash - filename + strlen (new_buf)] = '\0'; + backtrace_free (state, new_buf, new_len, error_callback, data); + filename = c; + new_buf = c; + new_len = clen; + } + } + + if (alc != NULL) + backtrace_free (state, alc, alc_len, error_callback, data); + alc = new_buf; + alc_len = new_len; + } + + /* Look for DEBUGLINK_NAME in the same directory as FILENAME. */ + + slash = strrchr (filename, '/'); + if (slash == NULL) + { + prefix = ""; + prefix_len = 0; + } + else + { + slash++; + prefix = filename; + prefix_len = slash - filename; + } + + ddescriptor = elf_try_debugfile (state, prefix, prefix_len, "", 0, + debuglink_name, error_callback, data); + if (ddescriptor >= 0) + { + ret = ddescriptor; + goto done; + } + + /* Look for DEBUGLINK_NAME in a .debug subdirectory of FILENAME. */ + + ddescriptor = elf_try_debugfile (state, prefix, prefix_len, ".debug/", + strlen (".debug/"), debuglink_name, + error_callback, data); + if (ddescriptor >= 0) + { + ret = ddescriptor; + goto done; + } + + /* Look for DEBUGLINK_NAME in /usr/lib/debug. */ + + ddescriptor = elf_try_debugfile (state, "/usr/lib/debug/", + strlen ("/usr/lib/debug/"), prefix, + prefix_len, debuglink_name, + error_callback, data); + if (ddescriptor >= 0) + ret = ddescriptor; + + done: + if (alc != NULL && alc_len > 0) + backtrace_free (state, alc, alc_len, error_callback, data); + return ret; +} + +/* Open a separate debug info file, using the debuglink section data + to find it. Returns an open file descriptor, or -1. */ + +static int +elf_open_debugfile_by_debuglink (struct backtrace_state *state, + const char *filename, + const char *debuglink_name, + uint32_t debuglink_crc, + backtrace_error_callback error_callback, + void *data) +{ + int ddescriptor; + uint32_t got_crc; + + ddescriptor = elf_find_debugfile_by_debuglink (state, filename, + debuglink_name, + error_callback, data); + if (ddescriptor < 0) + return -1; + + got_crc = elf_crc32_file (state, ddescriptor, error_callback, data); + if (got_crc != debuglink_crc) + { + backtrace_close (ddescriptor, error_callback, data); + return -1; + } + + return ddescriptor; +} + /* Add the backtrace data for one ELF file. Returns 1 on success, 0 on failure (in both cases descriptor is closed) or -1 if exe is non-zero and the ELF file is ET_DYN, which tells the caller that @@ -517,9 +972,10 @@ elf_syminfo (struct backtrace_state *state, uintptr_t addr, base_address is determined. */ static int -elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, - backtrace_error_callback error_callback, void *data, - fileline *fileline_fn, int *found_sym, int *found_dwarf, int exe) +elf_add (struct backtrace_state *state, const char *filename, int descriptor, + uintptr_t base_address, backtrace_error_callback error_callback, + void *data, fileline *fileline_fn, int *found_sym, int *found_dwarf, + int exe, int debuginfo) { struct backtrace_view ehdr_view; b_elf_ehdr ehdr; @@ -543,6 +999,14 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, int symtab_view_valid; struct backtrace_view strtab_view; int strtab_view_valid; + struct backtrace_view buildid_view; + int buildid_view_valid; + const char *buildid_data; + uint32_t buildid_size; + struct backtrace_view debuglink_view; + int debuglink_view_valid; + const char *debuglink_name; + uint32_t debuglink_crc; off_t min_offset; off_t max_offset; struct backtrace_view debug_view; @@ -555,6 +1019,12 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, names_view_valid = 0; symtab_view_valid = 0; strtab_view_valid = 0; + buildid_view_valid = 0; + buildid_data = NULL; + buildid_size = 0; + debuglink_view_valid = 0; + debuglink_name = NULL; + debuglink_crc = 0; debug_view_valid = 0; if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback, @@ -707,11 +1177,61 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, break; } } + + /* Read the build ID if present. This could check for any + SHT_NOTE section with the right note name and type, but gdb + looks for a specific section name. */ + if (!debuginfo + && !buildid_view_valid + && strcmp (name, ".note.gnu.build-id") == 0) + { + const b_elf_note *note; + + if (!backtrace_get_view (state, descriptor, shdr->sh_offset, + shdr->sh_size, error_callback, data, + &buildid_view)) + goto fail; + + buildid_view_valid = 1; + note = (const b_elf_note *) buildid_view.data; + if (note->type == NT_GNU_BUILD_ID + && note->namesz == 4 + && strncmp (note->name, "GNU", 4) == 0 + && shdr->sh_size < 12 + ((note->namesz + 3) & ~ 3) + note->descsz) + { + buildid_data = ¬e->name[0] + ((note->namesz + 3) & ~ 3); + buildid_size = note->descsz; + } + } + + /* Read the debuglink file if present. */ + if (!debuginfo + && !debuglink_view_valid + && strcmp (name, ".gnu_debuglink") == 0) + { + const char *debuglink_data; + size_t crc_offset; + + if (!backtrace_get_view (state, descriptor, shdr->sh_offset, + shdr->sh_size, error_callback, data, + &debuglink_view)) + goto fail; + + debuglink_view_valid = 1; + debuglink_data = (const char *) debuglink_view.data; + crc_offset = strnlen (debuglink_data, shdr->sh_size); + crc_offset = (crc_offset + 3) & ~3; + if (crc_offset + 4 <= shdr->sh_size) + { + debuglink_name = debuglink_data; + debuglink_crc = *(const uint32_t*)(debuglink_data + crc_offset); + } + } } if (symtab_shndx == 0) symtab_shndx = dynsym_shndx; - if (symtab_shndx != 0) + if (symtab_shndx != 0 && !debuginfo) { const b_elf_shdr *symtab_shdr; unsigned int strtab_shndx; @@ -757,6 +1277,7 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, /* We no longer need the symbol table, but we hold on to the string table permanently. */ backtrace_release_view (state, &symtab_view, error_callback, data); + symtab_view_valid = 0; *found_sym = 1; @@ -770,6 +1291,53 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, backtrace_release_view (state, &names_view, error_callback, data); names_view_valid = 0; + /* If the debug info is in a separate file, read that one instead. */ + + if (buildid_data != NULL) + { + int d; + + d = elf_open_debugfile_by_buildid (state, buildid_data, buildid_size, + error_callback, data); + if (d >= 0) + { + backtrace_release_view (state, &buildid_view, error_callback, data); + if (debuglink_view_valid) + backtrace_release_view (state, &debuglink_view, error_callback, + data); + return elf_add (state, NULL, d, base_address, error_callback, data, + fileline_fn, found_sym, found_dwarf, 0, 1); + } + } + + if (buildid_view_valid) + { + backtrace_release_view (state, &buildid_view, error_callback, data); + buildid_view_valid = 0; + } + + if (debuglink_name != NULL) + { + int d; + + d = elf_open_debugfile_by_debuglink (state, filename, debuglink_name, + debuglink_crc, error_callback, + data); + if (d >= 0) + { + backtrace_release_view (state, &debuglink_view, error_callback, + data); + return elf_add (state, NULL, d, base_address, error_callback, data, + fileline_fn, found_sym, found_dwarf, 0, 1); + } + } + + if (debuglink_view_valid) + { + backtrace_release_view (state, &debuglink_view, error_callback, data); + debuglink_view_valid = 0; + } + /* Read all the debug sections in a single view, since they are probably adjacent in the file. We never release this view. */ @@ -842,6 +1410,10 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, backtrace_release_view (state, &symtab_view, error_callback, data); if (strtab_view_valid) backtrace_release_view (state, &strtab_view, error_callback, data); + if (debuglink_view_valid) + backtrace_release_view (state, &debuglink_view, error_callback, data); + if (buildid_view_valid) + backtrace_release_view (state, &buildid_view, error_callback, data); if (debug_view_valid) backtrace_release_view (state, &debug_view, error_callback, data); if (descriptor != -1) @@ -859,6 +1431,7 @@ struct phdr_data fileline *fileline_fn; int *found_sym; int *found_dwarf; + const char *exe_filename; int exe_descriptor; }; @@ -873,6 +1446,7 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, void *pdata) { struct phdr_data *pd = (struct phdr_data *) pdata; + const char *filename; int descriptor; int does_not_exist; fileline elf_fileline_fn; @@ -885,6 +1459,7 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, { if (pd->exe_descriptor == -1) return 0; + filename = pd->exe_filename; descriptor = pd->exe_descriptor; pd->exe_descriptor = -1; } @@ -896,14 +1471,16 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, pd->exe_descriptor = -1; } + filename = info->dlpi_name; descriptor = backtrace_open (info->dlpi_name, pd->error_callback, pd->data, &does_not_exist); if (descriptor < 0) return 0; } - if (elf_add (pd->state, descriptor, info->dlpi_addr, pd->error_callback, - pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf, 0)) + if (elf_add (pd->state, filename, descriptor, info->dlpi_addr, + pd->error_callback, pd->data, &elf_fileline_fn, pd->found_sym, + &found_dwarf, 0, 0)) { if (found_dwarf) { @@ -920,8 +1497,8 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, sections. */ int -backtrace_initialize (struct backtrace_state *state, int descriptor, - backtrace_error_callback error_callback, +backtrace_initialize (struct backtrace_state *state, const char *filename, + int descriptor, backtrace_error_callback error_callback, void *data, fileline *fileline_fn) { int ret; @@ -930,8 +1507,8 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, fileline elf_fileline_fn = elf_nodebug; struct phdr_data pd; - ret = elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn, - &found_sym, &found_dwarf, 1); + ret = elf_add (state, filename, descriptor, 0, error_callback, data, + &elf_fileline_fn, &found_sym, &found_dwarf, 1, 0); if (!ret) return 0; @@ -941,6 +1518,7 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, pd.fileline_fn = &elf_fileline_fn; pd.found_sym = &found_sym; pd.found_dwarf = &found_dwarf; + pd.exe_filename = filename; pd.exe_descriptor = ret < 0 ? descriptor : -1; dl_iterate_phdr (phdr_callback, (void *) &pd); diff --git a/libbacktrace/fileline.c b/libbacktrace/fileline.c index 674fbba..dddb735 100644 --- a/libbacktrace/fileline.c +++ b/libbacktrace/fileline.c @@ -58,6 +58,7 @@ fileline_initialize (struct backtrace_state *state, int pass; int called_error_callback; int descriptor; + const char *filename; char buf[64]; if (!state->threaded) @@ -84,7 +85,6 @@ fileline_initialize (struct backtrace_state *state, called_error_callback = 0; for (pass = 0; pass < 5; ++pass) { - const char *filename; int does_not_exist; switch (pass) @@ -140,8 +140,8 @@ fileline_initialize (struct backtrace_state *state, if (!failed) { - if (!backtrace_initialize (state, descriptor, error_callback, data, - &fileline_fn)) + if (!backtrace_initialize (state, filename, descriptor, error_callback, + data, &fileline_fn)) failed = 1; } diff --git a/libbacktrace/internal.h b/libbacktrace/internal.h index 73728da..0a35921 100644 --- a/libbacktrace/internal.h +++ b/libbacktrace/internal.h @@ -268,6 +268,7 @@ extern int backtrace_vector_release (struct backtrace_state *state, appropriate one. */ extern int backtrace_initialize (struct backtrace_state *state, + const char *filename, int descriptor, backtrace_error_callback error_callback, void *data, diff --git a/libbacktrace/pecoff.c b/libbacktrace/pecoff.c index c7d32aa..8971b8a 100644 --- a/libbacktrace/pecoff.c +++ b/libbacktrace/pecoff.c @@ -890,7 +890,8 @@ coff_add (struct backtrace_state *state, int descriptor, sections. */ int -backtrace_initialize (struct backtrace_state *state, int descriptor, +backtrace_initialize (struct backtrace_state *state, + const char *filename ATTRIBUTE_UNUSED, int descriptor, backtrace_error_callback error_callback, void *data, fileline *fileline_fn) { diff --git a/libbacktrace/unknown.c b/libbacktrace/unknown.c index 8d06c31..38a570f 100644 --- a/libbacktrace/unknown.c +++ b/libbacktrace/unknown.c @@ -54,6 +54,7 @@ unknown_fileline (struct backtrace_state *state ATTRIBUTE_UNUSED, int backtrace_initialize (struct backtrace_state *state ATTRIBUTE_UNUSED, + const char *filename ATTRIBUTE_UNUSED, int descriptor ATTRIBUTE_UNUSED, backtrace_error_callback error_callback ATTRIBUTE_UNUSED, void *data ATTRIBUTE_UNUSED, fileline *fileline_fn) diff --git a/libbacktrace/xcoff.c b/libbacktrace/xcoff.c index b3d7e24..f752391 100644 --- a/libbacktrace/xcoff.c +++ b/libbacktrace/xcoff.c @@ -1434,7 +1434,8 @@ xcoff_add_shared_libs (struct backtrace_state *state, Returns 1 on success, 0 on failure. */ int -backtrace_initialize (struct backtrace_state *state, int descriptor, +backtrace_initialize (struct backtrace_state *state, + const char *filename ATTRIBUTE_UNUSED, int descriptor, backtrace_error_callback error_callback, void *data, fileline *fileline_fn) { -- 2.7.4 From 3d2150aca3ca8317dc9b061fe88ff8ee47d3f3f2 Mon Sep 17 00:00:00 2001 From: Denis Khalikov Date: Thu, 7 Jun 2018 15:36:22 +0300 Subject: [PATCH 06/16] PR other/67165 * elf.c (__builtin_prefetch): Define if not __GNUC__. (unlikely): Define. (SHF_UNCOMPRESSED, ELFCOMPRESS_ZLIB): Define. (b_elf_chdr): Define type. (enum debug_section): Add ZDEBUG_xxx values. (debug_section_names): Add names for new sections. (struct debug_section_info): Add compressed field. (elf_zlib_failed, elf_zlib_fetch): New static functions. (HUFFMAN_TABLE_SIZE, HUFFMAN_VALUE_MASK): Define. (HUFFMAN_BITS_SHIFT, HUFFMAN_BITS_MASK): Define. (HUFFMAN_SECONDARY_SHIFT): Define. (ZDEBUG_TABLE_SIZE): Define. (ZDEBUG_TABLE_CODELEN_OFFSET, ZDEBUG_TABLE_WORK_OFFSET): Define. (final_next_secondary): New static variable if BACKTRACE_GENERATE_FIXED_HUFFMAN_TABLE. (elf_zlib_inflate_table): New static function. (BACKTRACE_GENERATE_FIXED_HUFFMAN_TABLE): If define, define main function to produce fixed Huffman table. (elf_zlib_default_table): New static variable. (elf_zlib_inflate): New static function. (elf_zlib_verify_checksum): Likewise. (elf_zlib_inflate_and_verify): Likewise. (elf_uncompress_zdebug): Likewise. (elf_uncompress_chdr): Likewise. (backtrace_uncompress_zdebug): New extern function. (elf_add): Look for .zdebug sections and SHF_COMPRESSED debug sections, and uncompress them. * internal.h (backtrace_compress_zdebug): Declare. * ztest.c: New file. * configure.ac: Check for -lz and check whether the linker supports --compress-debug-sections. * Makefile.am (ztest_SOURCES): New variable. (ztest_CFLAGS, ztest_LDADD): New variables. (check_PROGRAMS): Add ztest. (ctestg_SOURCES): New variable. (ctestg_CFLAGS, ctestg_LDFLAGS, ctestg_LDADD): New variables. (ctesta_SOURCES): New variable. (ctesta_CFLAGS, ctesta_LDFLAGS, ctesta_LDADD): New variables. (check_PROGRAMS): Add ctestg and ctesta. * configure, config.h.in, Makefile.in: Rebuild. * xcoff.c: Initial support for DWARF debug sections in XCOFF. (STYP_DWARF, SSUBTYP_DW*): Define. (enum dwarf_section): Define. (struct dwsect_info): Define. (xcoff_add): Look for DWARF sections, pass them to backtrace_dwarf_add. * configure.ac: Check for clock_gettime. * config.h.in: Regenerate. * configure: Likewise. * ztest.c (average_time, test_large): Conditionalize test timing on clock_gettime availability. * Makefile.am: Append the content of clock_gettime_link to ztest_LDADD. * configure.ac: Test for the case that clock_gettime is in librt. * Makefile.in: Regenerate. * configure: Likewise. * ztest.c: #include . (TEST_TIMING): Don't define, don't test. (xclock_gettime, xclockid_t): Define if !HAVE_CLOCK_GETTIME. (clockid_t, clock_gettime, CLOCK_REALTIME): Likewise. (ZLIB_CLOCK_GETTIME_ARG): Define. * configure.ac: Change clock_gettime_link to CLOCK_GETTIME_LINK. * Makefile.am: Likewise. * configure, Makefile.in: Rebuild. * elf.c (elf_zlib_fetch): Change pval argument to uint64_t *. Read a four byte integer. (elf_zlib_inflate): Change val to uint64_t. Align pin to a 32-bit boundary before ever calling elf_zlib_fetch. * ztest.c (test_large): Simplify print statements a bit. * ztest.c (test_large): Pass unsigned long *, not size_t *, to zlib uncompress function. * configure.ac: Add CET_FLAGS to EXTRA_FLAGS. * aclocal.m4: Regenerate. * Makefile.in: Likewise. * configure: Likewise. * elf.c (codes) [GENERATE_FIXED_HUFFMAN_TABLE]: Fix size to be 288. (main) [GENERATE_FIXED_HUFFMAN_TABLE]: Pass 288 to elf_zlib_inflate_table. Generate elf_zlib_default_dist_table. (elf_zlib_default_table): Update. (elf_zlib_default_dist_table): New static array. (elf_zlib_inflate): Use elf_zlib_default_dist_table for dist table for block type 1. * ztest.c (struct zlib_test): Add uncompressed_len. (tests): Initialize uncompressed_len field. Add new test case. (test_samples): Use uncompressed_len field. Change-Id: I1dd4780768695f66177983c50942001365ff75ed --- libbacktrace/ChangeLog | 114 +++ libbacktrace/Makefile.am | 27 + libbacktrace/Makefile.in | 108 ++- libbacktrace/aclocal.m4 | 2 + libbacktrace/config.h.in | 9 + libbacktrace/configure | 252 ++++++- libbacktrace/configure.ac | 38 + libbacktrace/elf.c | 1712 ++++++++++++++++++++++++++++++++++++++++++++- libbacktrace/internal.h | 9 + libbacktrace/xcoff.c | 144 +++- libbacktrace/ztest.c | 537 ++++++++++++++ 11 files changed, 2925 insertions(+), 27 deletions(-) create mode 100644 libbacktrace/ztest.c diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog index 1c72eb0..ad0ab3c 100644 --- a/libbacktrace/ChangeLog +++ b/libbacktrace/ChangeLog @@ -1,3 +1,117 @@ +2018-01-16 Ian Lance Taylor + + * elf.c (codes) [GENERATE_FIXED_HUFFMAN_TABLE]: Fix size to be + 288. + (main) [GENERATE_FIXED_HUFFMAN_TABLE]: Pass 288 to + elf_zlib_inflate_table. Generate elf_zlib_default_dist_table. + (elf_zlib_default_table): Update. + (elf_zlib_default_dist_table): New static array. + (elf_zlib_inflate): Use elf_zlib_default_dist_table for dist table + for block type 1. + * ztest.c (struct zlib_test): Add uncompressed_len. + (tests): Initialize uncompressed_len field. Add new test case. + (test_samples): Use uncompressed_len field. + +2017-11-17 Igor Tsimbalist + + * configure.ac: Add CET_FLAGS to EXTRA_FLAGS. + * aclocal.m4: Regenerate. + * Makefile.in: Likewise. + * configure: Likewise. + +2017-10-06 Ian Lance Taylor + + * ztest.c (test_large): Pass unsigned long *, not size_t *, to + zlib uncompress function. + +2017-10-05 Ian Lance Taylor + + * elf.c (elf_zlib_fetch): Change pval argument to uint64_t *. + Read a four byte integer. + (elf_zlib_inflate): Change val to uint64_t. Align pin to a 32-bit + boundary before ever calling elf_zlib_fetch. + * ztest.c (test_large): Simplify print statements a bit. + +2017-10-02 Ian Lance Taylor + + * ztest.c: #include . + (TEST_TIMING): Don't define, don't test. + (xclock_gettime, xclockid_t): Define if !HAVE_CLOCK_GETTIME. + (clockid_t, clock_gettime, CLOCK_REALTIME): Likewise. + (ZLIB_CLOCK_GETTIME_ARG): Define. + * configure.ac: Change clock_gettime_link to CLOCK_GETTIME_LINK. + * Makefile.am: Likewise. + * configure, Makefile.in: Rebuild. + +2017-10-02 Thomas Schwinge + + PR other/67165 + * Makefile.am: Append the content of clock_gettime_link to + ztest_LDADD. + * configure.ac: Test for the case that clock_gettime is in librt. + * Makefile.in: Regenerate. + * configure: Likewise. + + PR other/67165 + * configure.ac: Check for clock_gettime. + * config.h.in: Regenerate. + * configure: Likewise. + * ztest.c (average_time, test_large): Conditionalize test timing + on clock_gettime availability. + +2017-09-29 Tony Reix + + * xcoff.c: Initial support for DWARF debug sections in XCOFF. + (STYP_DWARF, SSUBTYP_DW*): Define. + (enum dwarf_section): Define. + (struct dwsect_info): Define. + (xcoff_add): Look for DWARF sections, pass them to + backtrace_dwarf_add. + +2017-09-28 Ian Lance Taylor + + PR other/67165 + * elf.c (__builtin_prefetch): Define if not __GNUC__. + (unlikely): Define. + (SHF_UNCOMPRESSED, ELFCOMPRESS_ZLIB): Define. + (b_elf_chdr): Define type. + (enum debug_section): Add ZDEBUG_xxx values. + (debug_section_names): Add names for new sections. + (struct debug_section_info): Add compressed field. + (elf_zlib_failed, elf_zlib_fetch): New static functions. + (HUFFMAN_TABLE_SIZE, HUFFMAN_VALUE_MASK): Define. + (HUFFMAN_BITS_SHIFT, HUFFMAN_BITS_MASK): Define. + (HUFFMAN_SECONDARY_SHIFT): Define. + (ZDEBUG_TABLE_SIZE): Define. + (ZDEBUG_TABLE_CODELEN_OFFSET, ZDEBUG_TABLE_WORK_OFFSET): Define. + (final_next_secondary): New static variable if + BACKTRACE_GENERATE_FIXED_HUFFMAN_TABLE. + (elf_zlib_inflate_table): New static function. + (BACKTRACE_GENERATE_FIXED_HUFFMAN_TABLE): If define, define main + function to produce fixed Huffman table. + (elf_zlib_default_table): New static variable. + (elf_zlib_inflate): New static function. + (elf_zlib_verify_checksum): Likewise. + (elf_zlib_inflate_and_verify): Likewise. + (elf_uncompress_zdebug): Likewise. + (elf_uncompress_chdr): Likewise. + (backtrace_uncompress_zdebug): New extern function. + (elf_add): Look for .zdebug sections and SHF_COMPRESSED debug + sections, and uncompress them. + * internal.h (backtrace_compress_zdebug): Declare. + * ztest.c: New file. + * configure.ac: Check for -lz and check whether the linker + supports --compress-debug-sections. + * Makefile.am (ztest_SOURCES): New variable. + (ztest_CFLAGS, ztest_LDADD): New variables. + (check_PROGRAMS): Add ztest. + (ctestg_SOURCES): New variable. + (ctestg_CFLAGS, ctestg_LDFLAGS, ctestg_LDADD): New variables. + (ctesta_SOURCES): New variable. + (ctesta_CFLAGS, ctesta_LDFLAGS, ctesta_LDADD): New variables. + (check_PROGRAMS): Add ctestg and ctesta. + * configure, config.h.in, Makefile.in: Rebuild. + 2017-09-22 Ian Lance Taylor PR sanitizer/77631 diff --git a/libbacktrace/Makefile.am b/libbacktrace/Makefile.am index 65635bf..7a26bdd 100644 --- a/libbacktrace/Makefile.am +++ b/libbacktrace/Makefile.am @@ -101,6 +101,17 @@ stest_LDADD = libbacktrace.la check_PROGRAMS += stest +ztest_SOURCES = ztest.c testlib.c +ztest_CFLAGS = -DSRCDIR=\"$(srcdir)\" +ztest_LDADD = libbacktrace.la + +if HAVE_ZLIB +ztest_LDADD += -lz +endif +ztest_LDADD += $(CLOCK_GETTIME_LINK) + +check_PROGRAMS += ztest + edtest_SOURCES = edtest.c edtest2_build.c testlib.c edtest_LDADD = libbacktrace.la @@ -132,6 +143,22 @@ dtest: btest endif HAVE_OBJCOPY_DEBUGLINK +if HAVE_COMPRESSED_DEBUG + +ctestg_SOURCES = btest.c testlib.c +ctestg_CFLAGS = $(AM_CFLAGS) -g +ctestg_LDFLAGS = -Wl,--compress-debug-sections=zlib-gnu +ctestg_LDADD = libbacktrace.la + +ctesta_SOURCES = btest.c testlib.c +ctesta_CFLAGS = $(AM_CFLAGS) -g +ctesta_LDFLAGS = -Wl,--compress-debug-sections=zlib-gabi +ctesta_LDADD = libbacktrace.la + +check_PROGRAMS += ctestg ctesta + +endif + endif NATIVE # We can't use automake's automatic dependency tracking, because it diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in index a659ec0..bd3f7b1 100644 --- a/libbacktrace/Makefile.in +++ b/libbacktrace/Makefile.in @@ -83,17 +83,21 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ -check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) -@NATIVE_TRUE@am__append_1 = btest stest edtest -@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__append_2 = ttest -@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_3 = dtest +check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) +@NATIVE_TRUE@am__append_1 = btest stest ztest edtest +@HAVE_ZLIB_TRUE@@NATIVE_TRUE@am__append_2 = -lz +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__append_3 = ttest +@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_4 = dtest +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@am__append_5 = ctestg ctesta subdir = . DIST_COMMON = README ChangeLog $(srcdir)/Makefile.in \ $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(srcdir)/config.h.in \ $(srcdir)/../mkinstalldirs $(srcdir)/backtrace-supported.h.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/../config/lead-dot.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/../config/cet.m4 \ + $(top_srcdir)/../config/enable.m4 \ + $(top_srcdir)/../config/lead-dot.m4 \ $(top_srcdir)/../config/multi.m4 \ $(top_srcdir)/../config/override.m4 \ $(top_srcdir)/../config/stdint.m4 \ @@ -116,8 +120,11 @@ am_libbacktrace_la_OBJECTS = atomic.lo dwarf.lo fileline.lo posix.lo \ print.lo sort.lo state.lo libbacktrace_la_OBJECTS = $(am_libbacktrace_la_OBJECTS) @NATIVE_TRUE@am__EXEEXT_1 = btest$(EXEEXT) stest$(EXEEXT) \ -@NATIVE_TRUE@ edtest$(EXEEXT) +@NATIVE_TRUE@ ztest$(EXEEXT) edtest$(EXEEXT) @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__EXEEXT_2 = ttest$(EXEEXT) +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@am__EXEEXT_3 = \ +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctestg$(EXEEXT) \ +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctesta$(EXEEXT) @NATIVE_TRUE@am_btest_OBJECTS = btest-btest.$(OBJEXT) \ @NATIVE_TRUE@ btest-testlib.$(OBJEXT) btest_OBJECTS = $(am_btest_OBJECTS) @@ -125,6 +132,22 @@ btest_OBJECTS = $(am_btest_OBJECTS) btest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(btest_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@am_ctesta_OBJECTS = ctesta-btest.$(OBJEXT) \ +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctesta-testlib.$(OBJEXT) +ctesta_OBJECTS = $(am_ctesta_OBJECTS) +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctesta_DEPENDENCIES = \ +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ libbacktrace.la +ctesta_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(ctesta_CFLAGS) $(CFLAGS) \ + $(ctesta_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@am_ctestg_OBJECTS = ctestg-btest.$(OBJEXT) \ +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctestg-testlib.$(OBJEXT) +ctestg_OBJECTS = $(am_ctestg_OBJECTS) +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctestg_DEPENDENCIES = \ +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ libbacktrace.la +ctestg_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(ctestg_CFLAGS) $(CFLAGS) \ + $(ctestg_LDFLAGS) $(LDFLAGS) -o $@ @NATIVE_TRUE@am_edtest_OBJECTS = edtest.$(OBJEXT) \ @NATIVE_TRUE@ edtest2_build.$(OBJEXT) testlib.$(OBJEXT) edtest_OBJECTS = $(am_edtest_OBJECTS) @@ -140,6 +163,14 @@ ttest_OBJECTS = $(am_ttest_OBJECTS) ttest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(ttest_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ +@NATIVE_TRUE@am_ztest_OBJECTS = ztest-ztest.$(OBJEXT) \ +@NATIVE_TRUE@ ztest-testlib.$(OBJEXT) +ztest_OBJECTS = $(am_ztest_OBJECTS) +@NATIVE_TRUE@ztest_DEPENDENCIES = libbacktrace.la \ +@NATIVE_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +ztest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(ztest_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = am__depfiles_maybe = @@ -153,8 +184,9 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ SOURCES = $(libbacktrace_la_SOURCES) $(EXTRA_libbacktrace_la_SOURCES) \ - $(btest_SOURCES) $(edtest_SOURCES) $(stest_SOURCES) \ - $(ttest_SOURCES) + $(btest_SOURCES) $(ctesta_SOURCES) $(ctestg_SOURCES) \ + $(edtest_SOURCES) $(stest_SOURCES) $(ttest_SOURCES) \ + $(ztest_SOURCES) MULTISRCTOP = MULTIBUILDTOP = MULTIDIRS = @@ -185,6 +217,7 @@ BACKTRACE_SUPPORTS_THREADS = @BACKTRACE_SUPPORTS_THREADS@ BACKTRACE_USES_MALLOC = @BACKTRACE_USES_MALLOC@ CC = @CC@ CFLAGS = @CFLAGS@ +CLOCK_GETTIME_LINK = @CLOCK_GETTIME_LINK@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ @@ -345,17 +378,29 @@ libbacktrace_la_LIBADD = \ $(ALLOC_FILE) libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD) -TESTS = $(check_PROGRAMS) $(am__append_3) +TESTS = $(check_PROGRAMS) $(am__append_4) @NATIVE_TRUE@btest_SOURCES = btest.c testlib.c @NATIVE_TRUE@btest_CFLAGS = $(AM_CFLAGS) -g -O @NATIVE_TRUE@btest_LDADD = libbacktrace.la @NATIVE_TRUE@stest_SOURCES = stest.c @NATIVE_TRUE@stest_LDADD = libbacktrace.la +@NATIVE_TRUE@ztest_SOURCES = ztest.c testlib.c +@NATIVE_TRUE@ztest_CFLAGS = -DSRCDIR=\"$(srcdir)\" +@NATIVE_TRUE@ztest_LDADD = libbacktrace.la $(am__append_2) \ +@NATIVE_TRUE@ $(CLOCK_GETTIME_LINK) @NATIVE_TRUE@edtest_SOURCES = edtest.c edtest2_build.c testlib.c @NATIVE_TRUE@edtest_LDADD = libbacktrace.la @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ttest_SOURCES = ttest.c testlib.c @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ttest_CFLAGS = $(AM_CFLAGS) -pthread @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ttest_LDADD = libbacktrace.la +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctestg_SOURCES = btest.c testlib.c +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctestg_CFLAGS = $(AM_CFLAGS) -g +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctestg_LDFLAGS = -Wl,--compress-debug-sections=zlib-gnu +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctestg_LDADD = libbacktrace.la +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctesta_SOURCES = btest.c testlib.c +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctesta_CFLAGS = $(AM_CFLAGS) -g +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctesta_LDFLAGS = -Wl,--compress-debug-sections=zlib-gabi +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctesta_LDADD = libbacktrace.la # We can't use automake's automatic dependency tracking, because it # breaks when using bootstrap-lean. Automatic dependency tracking @@ -448,6 +493,12 @@ clean-checkPROGRAMS: btest$(EXEEXT): $(btest_OBJECTS) $(btest_DEPENDENCIES) $(EXTRA_btest_DEPENDENCIES) @rm -f btest$(EXEEXT) $(btest_LINK) $(btest_OBJECTS) $(btest_LDADD) $(LIBS) +ctesta$(EXEEXT): $(ctesta_OBJECTS) $(ctesta_DEPENDENCIES) $(EXTRA_ctesta_DEPENDENCIES) + @rm -f ctesta$(EXEEXT) + $(ctesta_LINK) $(ctesta_OBJECTS) $(ctesta_LDADD) $(LIBS) +ctestg$(EXEEXT): $(ctestg_OBJECTS) $(ctestg_DEPENDENCIES) $(EXTRA_ctestg_DEPENDENCIES) + @rm -f ctestg$(EXEEXT) + $(ctestg_LINK) $(ctestg_OBJECTS) $(ctestg_LDADD) $(LIBS) edtest$(EXEEXT): $(edtest_OBJECTS) $(edtest_DEPENDENCIES) $(EXTRA_edtest_DEPENDENCIES) @rm -f edtest$(EXEEXT) $(LINK) $(edtest_OBJECTS) $(edtest_LDADD) $(LIBS) @@ -457,6 +508,9 @@ stest$(EXEEXT): $(stest_OBJECTS) $(stest_DEPENDENCIES) $(EXTRA_stest_DEPENDENCIE ttest$(EXEEXT): $(ttest_OBJECTS) $(ttest_DEPENDENCIES) $(EXTRA_ttest_DEPENDENCIES) @rm -f ttest$(EXEEXT) $(ttest_LINK) $(ttest_OBJECTS) $(ttest_LDADD) $(LIBS) +ztest$(EXEEXT): $(ztest_OBJECTS) $(ztest_DEPENDENCIES) $(EXTRA_ztest_DEPENDENCIES) + @rm -f ztest$(EXEEXT) + $(ztest_LINK) $(ztest_OBJECTS) $(ztest_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -485,6 +539,30 @@ btest-testlib.o: testlib.c btest-testlib.obj: testlib.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(btest_CFLAGS) $(CFLAGS) -c -o btest-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` +ctesta-btest.o: btest.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ctesta_CFLAGS) $(CFLAGS) -c -o ctesta-btest.o `test -f 'btest.c' || echo '$(srcdir)/'`btest.c + +ctesta-btest.obj: btest.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ctesta_CFLAGS) $(CFLAGS) -c -o ctesta-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi` + +ctesta-testlib.o: testlib.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ctesta_CFLAGS) $(CFLAGS) -c -o ctesta-testlib.o `test -f 'testlib.c' || echo '$(srcdir)/'`testlib.c + +ctesta-testlib.obj: testlib.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ctesta_CFLAGS) $(CFLAGS) -c -o ctesta-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` + +ctestg-btest.o: btest.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ctestg_CFLAGS) $(CFLAGS) -c -o ctestg-btest.o `test -f 'btest.c' || echo '$(srcdir)/'`btest.c + +ctestg-btest.obj: btest.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ctestg_CFLAGS) $(CFLAGS) -c -o ctestg-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi` + +ctestg-testlib.o: testlib.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ctestg_CFLAGS) $(CFLAGS) -c -o ctestg-testlib.o `test -f 'testlib.c' || echo '$(srcdir)/'`testlib.c + +ctestg-testlib.obj: testlib.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ctestg_CFLAGS) $(CFLAGS) -c -o ctestg-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` + ttest-ttest.o: ttest.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ttest_CFLAGS) $(CFLAGS) -c -o ttest-ttest.o `test -f 'ttest.c' || echo '$(srcdir)/'`ttest.c @@ -497,6 +575,18 @@ ttest-testlib.o: testlib.c ttest-testlib.obj: testlib.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ttest_CFLAGS) $(CFLAGS) -c -o ttest-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` +ztest-ztest.o: ztest.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ztest_CFLAGS) $(CFLAGS) -c -o ztest-ztest.o `test -f 'ztest.c' || echo '$(srcdir)/'`ztest.c + +ztest-ztest.obj: ztest.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ztest_CFLAGS) $(CFLAGS) -c -o ztest-ztest.obj `if test -f 'ztest.c'; then $(CYGPATH_W) 'ztest.c'; else $(CYGPATH_W) '$(srcdir)/ztest.c'; fi` + +ztest-testlib.o: testlib.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ztest_CFLAGS) $(CFLAGS) -c -o ztest-testlib.o `test -f 'testlib.c' || echo '$(srcdir)/'`testlib.c + +ztest-testlib.obj: testlib.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ztest_CFLAGS) $(CFLAGS) -c -o ztest-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` + mostlyclean-libtool: -rm -f *.lo diff --git a/libbacktrace/aclocal.m4 b/libbacktrace/aclocal.m4 index 8e84ddd..7a6ea55 100644 --- a/libbacktrace/aclocal.m4 +++ b/libbacktrace/aclocal.m4 @@ -670,6 +670,8 @@ AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR +m4_include([../config/cet.m4]) +m4_include([../config/enable.m4]) m4_include([../config/lead-dot.m4]) m4_include([../config/multi.m4]) m4_include([../config/override.m4]) diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in index 1c38e8e..c19b6e4 100644 --- a/libbacktrace/config.h.in +++ b/libbacktrace/config.h.in @@ -9,6 +9,9 @@ /* Define to 1 if you have the __atomic functions */ #undef HAVE_ATOMIC_FUNCTIONS +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + /* Define to 1 if you have the declaration of `strnlen', and to 0 if you don't. */ #undef HAVE_DECL_STRNLEN @@ -31,6 +34,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the `z' library (-lz). */ +#undef HAVE_LIBZ + /* Define to 1 if you have the header file. */ #undef HAVE_LINK_H @@ -76,6 +82,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H +/* Define if -lz is available. */ +#undef HAVE_ZLIB + /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #undef LT_OBJDIR diff --git a/libbacktrace/configure b/libbacktrace/configure index 785fcbe..a89550f 100755 --- a/libbacktrace/configure +++ b/libbacktrace/configure @@ -607,9 +607,14 @@ NATIVE_TRUE HAVE_OBJCOPY_DEBUGLINK_FALSE HAVE_OBJCOPY_DEBUGLINK_TRUE OBJCOPY +HAVE_COMPRESSED_DEBUG_FALSE +HAVE_COMPRESSED_DEBUG_TRUE +HAVE_ZLIB_FALSE +HAVE_ZLIB_TRUE HAVE_PTHREAD_FALSE HAVE_PTHREAD_TRUE PTHREAD_CFLAGS +CLOCK_GETTIME_LINK BACKTRACE_USES_MALLOC ALLOC_FILE VIEW_FILE @@ -738,6 +743,7 @@ enable_fast_install with_gnu_ld enable_libtool_lock enable_largefile +enable_cet with_system_libunwind enable_host_shared ' @@ -1380,6 +1386,8 @@ Optional Features: optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --disable-largefile omit support for large files + --enable-cet enable Intel CET in target libraries + [default=default] --enable-host-shared build host code as shared libraries Optional Packages: @@ -11141,7 +11149,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11144 "configure" +#line 11152 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11247,7 +11255,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11250 "configure" +#line 11258 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11779,6 +11787,77 @@ $as_echo "$libbacktrace_cv_c_random_seed_string" >&6; } fi fi +if test -n "${with_target_subdir}"; then + # Add CET specific flags is Intel CET is enabled. + # Check whether --enable-cet was given. +if test "${enable_cet+set}" = set; then : + enableval=$enable_cet; + case "$enableval" in + yes|no|default) ;; + *) as_fn_error "Unknown argument to enable/disable cet" "$LINENO" 5 ;; + esac + +else + enable_cet=default +fi + + +case "$host" in + i[34567]86-*-linux* | x86_64-*-linux*) + case "$enable_cet" in + default) + # Check if assembler supports CET. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +asm ("setssbsy"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + enable_cet=yes +else + enable_cet=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ;; + yes) + # Check if assembler supports CET. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +asm ("setssbsy"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + as_fn_error "assembler with CET support is required for --enable-cet" "$LINENO" 5 +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ;; + esac + ;; + *) + enable_cet=no + ;; +esac +if test x$enable_cet = xyes; then + CET_FLAGS="-fcf-protection -mcet" +fi + + EXTRA_FLAGS="$EXTRA_FLAGS $CET_FLAGS" +fi + ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -12743,6 +12822,70 @@ $as_echo "#define HAVE_GETEXECNAME 1" >>confdefs.h fi +# Check for the clock_gettime function. +for ac_func in clock_gettime +do : + ac_fn_c_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime" +if test "x$ac_cv_func_clock_gettime" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_CLOCK_GETTIME 1 +_ACEOF + +fi +done + +clock_gettime_link= +# At least for glibc, clock_gettime is in librt. But don't +# pull that in if it still doesn't give us the function we want. This +# test is copied from libgomp, and modified to not link in -lrt as +# we're using this for test timing only. +if test "$ac_cv_func_clock_gettime" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5 +$as_echo_n "checking for clock_gettime in -lrt... " >&6; } +if test "${ac_cv_lib_rt_clock_gettime+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lrt $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char clock_gettime (); +int +main () +{ +return clock_gettime (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_rt_clock_gettime=yes +else + ac_cv_lib_rt_clock_gettime=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5 +$as_echo "$ac_cv_lib_rt_clock_gettime" >&6; } +if test "x$ac_cv_lib_rt_clock_gettime" = x""yes; then : + CLOCK_GETTIME_LINK=-lrt + +$as_echo "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h + +fi + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -pthread is supported" >&5 $as_echo_n "checking whether -pthread is supported... " >&6; } if test "${libgo_cv_lib_pthread+set}" = set; then : @@ -12779,6 +12922,103 @@ else fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compress in -lz" >&5 +$as_echo_n "checking for compress in -lz... " >&6; } +if test "${ac_cv_lib_z_compress+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char compress (); +int +main () +{ +return compress (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_z_compress=yes +else + ac_cv_lib_z_compress=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_compress" >&5 +$as_echo "$ac_cv_lib_z_compress" >&6; } +if test "x$ac_cv_lib_z_compress" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBZ 1 +_ACEOF + + LIBS="-lz $LIBS" + +fi + +if test $ac_cv_lib_z_compress = "yes"; then + +$as_echo "#define HAVE_ZLIB 1" >>confdefs.h + +fi + if test "$ac_cv_lib_z_compress" = yes; then + HAVE_ZLIB_TRUE= + HAVE_ZLIB_FALSE='#' +else + HAVE_ZLIB_TRUE='#' + HAVE_ZLIB_FALSE= +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether --compress-debug-sections is supported" >&5 +$as_echo_n "checking whether --compress-debug-sections is supported... " >&6; } +if test "${libgo_cv_ld_compress+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + LDFLAGS_hold=$LDFLAGS +LDFLAGS="$LDFLAGS -Wl,--compress-debug-sections=zlib-gnu" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + libgo_cv_ld_compress=yes +else + libgo_cv_ld_compress=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LDFLAGS=$LDFLAGS_hold +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libgo_cv_ld_compress" >&5 +$as_echo "$libgo_cv_ld_compress" >&6; } + if test "$libgo_cv_ld_compress" = yes; then + HAVE_COMPRESSED_DEBUG_TRUE= + HAVE_COMPRESSED_DEBUG_FALSE='#' +else + HAVE_COMPRESSED_DEBUG_TRUE='#' + HAVE_COMPRESSED_DEBUG_FALSE= +fi + + # Extract the first word of "objcopy", so it can be a program name with args. set dummy objcopy; ac_word=$2 @@ -13008,6 +13248,14 @@ if test -z "${HAVE_PTHREAD_TRUE}" && test -z "${HAVE_PTHREAD_FALSE}"; then as_fn_error "conditional \"HAVE_PTHREAD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${HAVE_ZLIB_TRUE}" && test -z "${HAVE_ZLIB_FALSE}"; then + as_fn_error "conditional \"HAVE_ZLIB\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_COMPRESSED_DEBUG_TRUE}" && test -z "${HAVE_COMPRESSED_DEBUG_FALSE}"; then + as_fn_error "conditional \"HAVE_COMPRESSED_DEBUG\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${HAVE_OBJCOPY_DEBUGLINK_TRUE}" && test -z "${HAVE_OBJCOPY_DEBUGLINK_FALSE}"; then as_fn_error "conditional \"HAVE_OBJCOPY_DEBUGLINK\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac index 34d1591..c238cd6 100644 --- a/libbacktrace/configure.ac +++ b/libbacktrace/configure.ac @@ -130,6 +130,12 @@ else EXTRA_FLAGS="$EXTRA_FLAGS -frandom-seed=\$@" fi fi + +if test -n "${with_target_subdir}"; then + # Add CET specific flags is Intel CET is enabled. + GCC_CET_FLAGS(CET_FLAGS) + EXTRA_FLAGS="$EXTRA_FLAGS $CET_FLAGS" +fi AC_SUBST(EXTRA_FLAGS) ACX_PROG_CC_WARNING_OPTS([-W -Wall -Wwrite-strings -Wstrict-prototypes \ @@ -388,6 +394,21 @@ if test "$have_getexecname" = "yes"; then AC_DEFINE(HAVE_GETEXECNAME, 1, [Define if getexecname is available.]) fi +# Check for the clock_gettime function. +AC_CHECK_FUNCS(clock_gettime) +clock_gettime_link= +# At least for glibc, clock_gettime is in librt. But don't +# pull that in if it still doesn't give us the function we want. This +# test is copied from libgomp, and modified to not link in -lrt as +# we're using this for test timing only. +if test "$ac_cv_func_clock_gettime" = no; then + AC_CHECK_LIB(rt, clock_gettime, + [CLOCK_GETTIME_LINK=-lrt + AC_DEFINE(HAVE_CLOCK_GETTIME, 1, + [Define to 1 if you have the `clock_gettime' function.])]) +fi +AC_SUBST(CLOCK_GETTIME_LINK) + dnl Test whether the compiler supports the -pthread option. AC_CACHE_CHECK([whether -pthread is supported], [libgo_cv_lib_pthread], @@ -405,6 +426,23 @@ AC_SUBST(PTHREAD_CFLAGS) AM_CONDITIONAL(HAVE_PTHREAD, test "$libgo_cv_lib_pthread" = yes) +AC_CHECK_LIB([z], [compress], []) +if test $ac_cv_lib_z_compress = "yes"; then + AC_DEFINE(HAVE_ZLIB, 1, [Define if -lz is available.]) +fi +AM_CONDITIONAL(HAVE_ZLIB, test "$ac_cv_lib_z_compress" = yes) + +dnl Test whether the linker supports the --compress_debug_sections option. +AC_CACHE_CHECK([whether --compress-debug-sections is supported], +[libgo_cv_ld_compress], +[LDFLAGS_hold=$LDFLAGS +LDFLAGS="$LDFLAGS -Wl,--compress-debug-sections=zlib-gnu" +AC_LINK_IFELSE([AC_LANG_PROGRAM(,)], +[libgo_cv_ld_compress=yes], +[libgo_cv_ld_compress=no]) +LDFLAGS=$LDFLAGS_hold]) +AM_CONDITIONAL(HAVE_COMPRESSED_DEBUG, test "$libgo_cv_ld_compress" = yes) + AC_ARG_VAR(OBJCOPY, [location of objcopy]) AC_CHECK_PROG(OBJCOPY, objcopy, objcopy,) AC_CACHE_CHECK([whether objcopy supports debuglink], diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c index 9452681..f923bc2 100644 --- a/libbacktrace/elf.c +++ b/libbacktrace/elf.c @@ -56,6 +56,13 @@ POSSIBILITY OF SUCH DAMAGE. */ #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) #endif +#ifndef __GNUC__ +#define __builtin_prefetch(p, r, l) +#define unlikely(x) (x) +#else +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + #if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN /* If strnlen is not declared, provide our own version. */ @@ -164,9 +171,11 @@ dl_iterate_phdr (int (*callback) (struct dl_phdr_info *, #undef SHT_SYMTAB #undef SHT_STRTAB #undef SHT_DYNSYM +#undef SHF_COMPRESSED #undef STT_OBJECT #undef STT_FUNC #undef NT_GNU_BUILD_ID +#undef ELFCOMPRESS_ZLIB /* Basic types. */ @@ -257,6 +266,8 @@ typedef struct { #define SHT_STRTAB 3 #define SHT_DYNSYM 11 +#define SHF_COMPRESSED 0x800 + #if BACKTRACE_ELF_SIZE == 32 typedef struct @@ -296,6 +307,29 @@ typedef struct #define NT_GNU_BUILD_ID 3 +#if BACKTRACE_ELF_SIZE == 32 + +typedef struct +{ + b_elf_word ch_type; /* Compresstion algorithm */ + b_elf_word ch_size; /* Uncompressed size */ + b_elf_word ch_addralign; /* Alignment for uncompressed data */ +} b_elf_chdr; /* Elf_Chdr */ + +#else /* BACKTRACE_ELF_SIZE != 32 */ + +typedef struct +{ + b_elf_word ch_type; /* Compression algorithm */ + b_elf_word ch_reserved; /* Reserved */ + b_elf_xword ch_size; /* Uncompressed size */ + b_elf_xword ch_addralign; /* Alignment for uncompressed data */ +} b_elf_chdr; /* Elf_Chdr */ + +#endif /* BACKTRACE_ELF_SIZE != 32 */ + +#define ELFCOMPRESS_ZLIB 1 + /* An index of ELF sections we care about. */ enum debug_section @@ -305,6 +339,15 @@ enum debug_section DEBUG_ABBREV, DEBUG_RANGES, DEBUG_STR, + + /* The old style compressed sections. This list must correspond to + the list of normal debug sections. */ + ZDEBUG_INFO, + ZDEBUG_LINE, + ZDEBUG_ABBREV, + ZDEBUG_RANGES, + ZDEBUG_STR, + DEBUG_MAX }; @@ -316,7 +359,12 @@ static const char * const debug_section_names[DEBUG_MAX] = ".debug_line", ".debug_abbrev", ".debug_ranges", - ".debug_str" + ".debug_str", + ".zdebug_info", + ".zdebug_line", + ".zdebug_abbrev", + ".zdebug_ranges", + ".zdebug_str" }; /* Information we gather for the sections we care about. */ @@ -329,6 +377,8 @@ struct debug_section_info size_t size; /* Section contents, after read from file. */ const unsigned char *data; + /* Whether the SHF_COMPRESSED flag is set for the section. */ + int compressed; }; /* Information we keep for an ELF symbol. */ @@ -965,6 +1015,1576 @@ elf_open_debugfile_by_debuglink (struct backtrace_state *state, return ddescriptor; } +/* A function useful for setting a breakpoint for an inflation failure + when this code is compiled with -g. */ + +static void +elf_zlib_failed(void) +{ +} + +/* *PVAL is the current value being read from the stream, and *PBITS + is the number of valid bits. Ensure that *PVAL holds at least 15 + bits by reading additional bits from *PPIN, up to PINEND, as + needed. Updates *PPIN, *PVAL and *PBITS. Returns 1 on success, 0 + on error. */ + +static int +elf_zlib_fetch (const unsigned char **ppin, const unsigned char *pinend, + uint64_t *pval, unsigned int *pbits) +{ + unsigned int bits; + const unsigned char *pin; + uint64_t val; + uint32_t next; + + bits = *pbits; + if (bits >= 15) + return 1; + pin = *ppin; + val = *pval; + + if (unlikely (pinend - pin < 4)) + { + elf_zlib_failed (); + return 0; + } + + /* We've ensured that PIN is aligned. */ + next = *(const uint32_t *)pin; + +#if __BYTE_ORDER == __ORDER_BIG_ENDIAN + next = __builtin_bswap32 (next); +#endif + + val |= (uint64_t)next << bits; + bits += 32; + pin += 4; + + /* We will need the next four bytes soon. */ + __builtin_prefetch (pin, 0, 0); + + *ppin = pin; + *pval = val; + *pbits = bits; + return 1; +} + +/* Huffman code tables, like the rest of the zlib format, are defined + by RFC 1951. We store a Huffman code table as a series of tables + stored sequentially in memory. Each entry in a table is 16 bits. + The first, main, table has 256 entries. It is followed by a set of + secondary tables of length 2 to 128 entries. The maximum length of + a code sequence in the deflate format is 15 bits, so that is all we + need. Each secondary table has an index, which is the offset of + the table in the overall memory storage. + + The deflate format says that all codes of a given bit length are + lexicographically consecutive. Perhaps we could have 130 values + that require a 15-bit code, perhaps requiring three secondary + tables of size 128. I don't know if this is actually possible, but + it suggests that the maximum size required for secondary tables is + 3 * 128 + 3 * 64 ... == 768. The zlib enough program reports 660 + as the maximum. We permit 768, since in addition to the 256 for + the primary table, with two bytes per entry, and with the two + tables we need, that gives us a page. + + A single table entry needs to store a value or (for the main table + only) the index and size of a secondary table. Values range from 0 + to 285, inclusive. Secondary table indexes, per above, range from + 0 to 510. For a value we need to store the number of bits we need + to determine that value (one value may appear multiple times in the + table), which is 1 to 8. For a secondary table we need to store + the number of bits used to index into the table, which is 1 to 7. + And of course we need 1 bit to decide whether we have a value or a + secondary table index. So each entry needs 9 bits for value/table + index, 3 bits for size, 1 bit what it is. For simplicity we use 16 + bits per entry. */ + +/* Number of entries we allocate to for one code table. We get a page + for the two code tables we need. */ + +#define HUFFMAN_TABLE_SIZE (1024) + +/* Bit masks and shifts for the values in the table. */ + +#define HUFFMAN_VALUE_MASK 0x01ff +#define HUFFMAN_BITS_SHIFT 9 +#define HUFFMAN_BITS_MASK 0x7 +#define HUFFMAN_SECONDARY_SHIFT 12 + +/* For working memory while inflating we need two code tables, we need + an array of code lengths (max value 15, so we use unsigned char), + and an array of unsigned shorts used while building a table. The + latter two arrays must be large enough to hold the maximum number + of code lengths, which RFC 1951 defines as 286 + 30. */ + +#define ZDEBUG_TABLE_SIZE \ + (2 * HUFFMAN_TABLE_SIZE * sizeof (uint16_t) \ + + (286 + 30) * sizeof (uint16_t) \ + + (286 + 30) * sizeof (unsigned char)) + +#define ZDEBUG_TABLE_CODELEN_OFFSET \ + (2 * HUFFMAN_TABLE_SIZE * sizeof (uint16_t) \ + + (286 + 30) * sizeof (uint16_t)) + +#define ZDEBUG_TABLE_WORK_OFFSET \ + (2 * HUFFMAN_TABLE_SIZE * sizeof (uint16_t)) + +#ifdef BACKTRACE_GENERATE_FIXED_HUFFMAN_TABLE + +/* Used by the main function that generates the fixed table to learn + the table size. */ +static size_t final_next_secondary; + +#endif + +/* Build a Huffman code table from an array of lengths in CODES of + length CODES_LEN. The table is stored into *TABLE. ZDEBUG_TABLE + is the same as for elf_zlib_inflate, used to find some work space. + Returns 1 on success, 0 on error. */ + +static int +elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, + uint16_t *zdebug_table, uint16_t *table) +{ + uint16_t count[16]; + uint16_t start[16]; + uint16_t prev[16]; + uint16_t firstcode[7]; + uint16_t *next; + size_t i; + size_t j; + unsigned int code; + size_t next_secondary; + + /* Count the number of code of each length. Set NEXT[val] to be the + next value after VAL with the same bit length. */ + + next = (uint16_t *) (((unsigned char *) zdebug_table) + + ZDEBUG_TABLE_WORK_OFFSET); + + memset (&count[0], 0, 16 * sizeof (uint16_t)); + for (i = 0; i < codes_len; ++i) + { + if (unlikely (codes[i] >= 16)) + { + elf_zlib_failed (); + return 0; + } + + if (count[codes[i]] == 0) + { + start[codes[i]] = i; + prev[codes[i]] = i; + } + else + { + next[prev[codes[i]]] = i; + prev[codes[i]] = i; + } + + ++count[codes[i]]; + } + + /* For each length, fill in the table for the codes of that + length. */ + + memset (table, 0, HUFFMAN_TABLE_SIZE * sizeof (uint16_t)); + + /* Handle the values that do not require a secondary table. */ + + code = 0; + for (j = 1; j <= 8; ++j) + { + unsigned int jcnt; + unsigned int val; + + jcnt = count[j]; + if (jcnt == 0) + continue; + + if (unlikely (jcnt > (1U << j))) + { + elf_zlib_failed (); + return 0; + } + + /* There are JCNT values that have this length, the values + starting from START[j] continuing through NEXT[VAL]. Those + values are assigned consecutive values starting at CODE. */ + + val = start[j]; + for (i = 0; i < jcnt; ++i) + { + uint16_t tval; + size_t ind; + unsigned int incr; + + /* In the compressed bit stream, the value VAL is encoded as + J bits with the value C. */ + + if (unlikely ((val & ~HUFFMAN_VALUE_MASK) != 0)) + { + elf_zlib_failed (); + return 0; + } + + tval = val | ((j - 1) << HUFFMAN_BITS_SHIFT); + + /* The table lookup uses 8 bits. If J is less than 8, we + don't know what the other bits will be. We need to fill + in all possibilities in the table. Since the Huffman + code is unambiguous, those entries can't be used for any + other code. */ + + for (ind = code; ind < 0x100; ind += 1 << j) + { + if (unlikely (table[ind] != 0)) + { + elf_zlib_failed (); + return 0; + } + table[ind] = tval; + } + + /* Advance to the next value with this length. */ + if (i + 1 < jcnt) + val = next[val]; + + /* The Huffman codes are stored in the bitstream with the + most significant bit first, as is required to make them + unambiguous. The effect is that when we read them from + the bitstream we see the bit sequence in reverse order: + the most significant bit of the Huffman code is the least + significant bit of the value we read from the bitstream. + That means that to make our table lookups work, we need + to reverse the bits of CODE. Since reversing bits is + tedious and in general requires using a table, we instead + increment CODE in reverse order. That is, if the number + of bits we are currently using, here named J, is 3, we + count as 000, 100, 010, 110, 001, 101, 011, 111, which is + to say the numbers from 0 to 7 but with the bits + reversed. Going to more bits, aka incrementing J, + effectively just adds more zero bits as the beginning, + and as such does not change the numeric value of CODE. + + To increment CODE of length J in reverse order, find the + most significant zero bit and set it to one while + clearing all higher bits. In other words, add 1 modulo + 2^J, only reversed. */ + + incr = 1U << (j - 1); + while ((code & incr) != 0) + incr >>= 1; + if (incr == 0) + code = 0; + else + { + code &= incr - 1; + code += incr; + } + } + } + + /* Handle the values that require a secondary table. */ + + /* Set FIRSTCODE, the number at which the codes start, for each + length. */ + + for (j = 9; j < 16; j++) + { + unsigned int jcnt; + unsigned int k; + + jcnt = count[j]; + if (jcnt == 0) + continue; + + /* There are JCNT values that have this length, the values + starting from START[j]. Those values are assigned + consecutive values starting at CODE. */ + + firstcode[j - 9] = code; + + /* Reverse add JCNT to CODE modulo 2^J. */ + for (k = 0; k < j; ++k) + { + if ((jcnt & (1U << k)) != 0) + { + unsigned int m; + unsigned int bit; + + bit = 1U << (j - k - 1); + for (m = 0; m < j - k; ++m, bit >>= 1) + { + if ((code & bit) == 0) + { + code += bit; + break; + } + code &= ~bit; + } + jcnt &= ~(1U << k); + } + } + if (unlikely (jcnt != 0)) + { + elf_zlib_failed (); + return 0; + } + } + + /* For J from 9 to 15, inclusive, we store COUNT[J] consecutive + values starting at START[J] with consecutive codes starting at + FIRSTCODE[J - 9]. In the primary table we need to point to the + secondary table, and the secondary table will be indexed by J - 9 + bits. We count down from 15 so that we install the larger + secondary tables first, as the smaller ones may be embedded in + the larger ones. */ + + next_secondary = 0; /* Index of next secondary table (after primary). */ + for (j = 15; j >= 9; j--) + { + unsigned int jcnt; + unsigned int val; + size_t primary; /* Current primary index. */ + size_t secondary; /* Offset to current secondary table. */ + size_t secondary_bits; /* Bit size of current secondary table. */ + + jcnt = count[j]; + if (jcnt == 0) + continue; + + val = start[j]; + code = firstcode[j - 9]; + primary = 0x100; + secondary = 0; + secondary_bits = 0; + for (i = 0; i < jcnt; ++i) + { + uint16_t tval; + size_t ind; + unsigned int incr; + + if ((code & 0xff) != primary) + { + uint16_t tprimary; + + /* Fill in a new primary table entry. */ + + primary = code & 0xff; + + tprimary = table[primary]; + if (tprimary == 0) + { + /* Start a new secondary table. */ + + if (unlikely ((next_secondary & HUFFMAN_VALUE_MASK) + != next_secondary)) + { + elf_zlib_failed (); + return 0; + } + + secondary = next_secondary; + secondary_bits = j - 8; + next_secondary += 1 << secondary_bits; + table[primary] = (secondary + + ((j - 8) << HUFFMAN_BITS_SHIFT) + + (1U << HUFFMAN_SECONDARY_SHIFT)); + } + else + { + /* There is an existing entry. It had better be a + secondary table with enough bits. */ + if (unlikely ((tprimary & (1U << HUFFMAN_SECONDARY_SHIFT)) + == 0)) + { + elf_zlib_failed (); + return 0; + } + secondary = tprimary & HUFFMAN_VALUE_MASK; + secondary_bits = ((tprimary >> HUFFMAN_BITS_SHIFT) + & HUFFMAN_BITS_MASK); + if (unlikely (secondary_bits < j - 8)) + { + elf_zlib_failed (); + return 0; + } + } + } + + /* Fill in secondary table entries. */ + + tval = val | ((j - 8) << HUFFMAN_BITS_SHIFT); + + for (ind = code >> 8; + ind < (1U << secondary_bits); + ind += 1U << (j - 8)) + { + if (unlikely (table[secondary + 0x100 + ind] != 0)) + { + elf_zlib_failed (); + return 0; + } + table[secondary + 0x100 + ind] = tval; + } + + if (i + 1 < jcnt) + val = next[val]; + + incr = 1U << (j - 1); + while ((code & incr) != 0) + incr >>= 1; + if (incr == 0) + code = 0; + else + { + code &= incr - 1; + code += incr; + } + } + } + +#ifdef BACKTRACE_GENERATE_FIXED_HUFFMAN_TABLE + final_next_secondary = next_secondary; +#endif + + return 1; +} + +#ifdef BACKTRACE_GENERATE_FIXED_HUFFMAN_TABLE + +/* Used to generate the fixed Huffman table for block type 1. */ + +#include + +static uint16_t table[ZDEBUG_TABLE_SIZE]; +static unsigned char codes[288]; + +int +main () +{ + size_t i; + + for (i = 0; i <= 143; ++i) + codes[i] = 8; + for (i = 144; i <= 255; ++i) + codes[i] = 9; + for (i = 256; i <= 279; ++i) + codes[i] = 7; + for (i = 280; i <= 287; ++i) + codes[i] = 8; + if (!elf_zlib_inflate_table (&codes[0], 288, &table[0], &table[0])) + { + fprintf (stderr, "elf_zlib_inflate_table failed\n"); + exit (EXIT_FAILURE); + } + + printf ("static const uint16_t elf_zlib_default_table[%#zx] =\n", + final_next_secondary + 0x100); + printf ("{\n"); + for (i = 0; i < final_next_secondary + 0x100; i += 8) + { + size_t j; + + printf (" "); + for (j = i; j < final_next_secondary + 0x100 && j < i + 8; ++j) + printf (" %#x,", table[j]); + printf ("\n"); + } + printf ("};\n"); + printf ("\n"); + + for (i = 0; i < 32; ++i) + codes[i] = 5; + if (!elf_zlib_inflate_table (&codes[0], 32, &table[0], &table[0])) + { + fprintf (stderr, "elf_zlib_inflate_table failed\n"); + exit (EXIT_FAILURE); + } + + printf ("static const uint16_t elf_zlib_default_dist_table[%#zx] =\n", + final_next_secondary + 0x100); + printf ("{\n"); + for (i = 0; i < final_next_secondary + 0x100; i += 8) + { + size_t j; + + printf (" "); + for (j = i; j < final_next_secondary + 0x100 && j < i + 8; ++j) + printf (" %#x,", table[j]); + printf ("\n"); + } + printf ("};\n"); + + return 0; +} + +#endif + +/* The fixed tables generated by the #ifdef'ed out main function + above. */ + +static const uint16_t elf_zlib_default_table[0x170] = +{ + 0xd00, 0xe50, 0xe10, 0xf18, 0xd10, 0xe70, 0xe30, 0x1230, + 0xd08, 0xe60, 0xe20, 0x1210, 0xe00, 0xe80, 0xe40, 0x1250, + 0xd04, 0xe58, 0xe18, 0x1200, 0xd14, 0xe78, 0xe38, 0x1240, + 0xd0c, 0xe68, 0xe28, 0x1220, 0xe08, 0xe88, 0xe48, 0x1260, + 0xd02, 0xe54, 0xe14, 0xf1c, 0xd12, 0xe74, 0xe34, 0x1238, + 0xd0a, 0xe64, 0xe24, 0x1218, 0xe04, 0xe84, 0xe44, 0x1258, + 0xd06, 0xe5c, 0xe1c, 0x1208, 0xd16, 0xe7c, 0xe3c, 0x1248, + 0xd0e, 0xe6c, 0xe2c, 0x1228, 0xe0c, 0xe8c, 0xe4c, 0x1268, + 0xd01, 0xe52, 0xe12, 0xf1a, 0xd11, 0xe72, 0xe32, 0x1234, + 0xd09, 0xe62, 0xe22, 0x1214, 0xe02, 0xe82, 0xe42, 0x1254, + 0xd05, 0xe5a, 0xe1a, 0x1204, 0xd15, 0xe7a, 0xe3a, 0x1244, + 0xd0d, 0xe6a, 0xe2a, 0x1224, 0xe0a, 0xe8a, 0xe4a, 0x1264, + 0xd03, 0xe56, 0xe16, 0xf1e, 0xd13, 0xe76, 0xe36, 0x123c, + 0xd0b, 0xe66, 0xe26, 0x121c, 0xe06, 0xe86, 0xe46, 0x125c, + 0xd07, 0xe5e, 0xe1e, 0x120c, 0xd17, 0xe7e, 0xe3e, 0x124c, + 0xd0f, 0xe6e, 0xe2e, 0x122c, 0xe0e, 0xe8e, 0xe4e, 0x126c, + 0xd00, 0xe51, 0xe11, 0xf19, 0xd10, 0xe71, 0xe31, 0x1232, + 0xd08, 0xe61, 0xe21, 0x1212, 0xe01, 0xe81, 0xe41, 0x1252, + 0xd04, 0xe59, 0xe19, 0x1202, 0xd14, 0xe79, 0xe39, 0x1242, + 0xd0c, 0xe69, 0xe29, 0x1222, 0xe09, 0xe89, 0xe49, 0x1262, + 0xd02, 0xe55, 0xe15, 0xf1d, 0xd12, 0xe75, 0xe35, 0x123a, + 0xd0a, 0xe65, 0xe25, 0x121a, 0xe05, 0xe85, 0xe45, 0x125a, + 0xd06, 0xe5d, 0xe1d, 0x120a, 0xd16, 0xe7d, 0xe3d, 0x124a, + 0xd0e, 0xe6d, 0xe2d, 0x122a, 0xe0d, 0xe8d, 0xe4d, 0x126a, + 0xd01, 0xe53, 0xe13, 0xf1b, 0xd11, 0xe73, 0xe33, 0x1236, + 0xd09, 0xe63, 0xe23, 0x1216, 0xe03, 0xe83, 0xe43, 0x1256, + 0xd05, 0xe5b, 0xe1b, 0x1206, 0xd15, 0xe7b, 0xe3b, 0x1246, + 0xd0d, 0xe6b, 0xe2b, 0x1226, 0xe0b, 0xe8b, 0xe4b, 0x1266, + 0xd03, 0xe57, 0xe17, 0xf1f, 0xd13, 0xe77, 0xe37, 0x123e, + 0xd0b, 0xe67, 0xe27, 0x121e, 0xe07, 0xe87, 0xe47, 0x125e, + 0xd07, 0xe5f, 0xe1f, 0x120e, 0xd17, 0xe7f, 0xe3f, 0x124e, + 0xd0f, 0xe6f, 0xe2f, 0x122e, 0xe0f, 0xe8f, 0xe4f, 0x126e, + 0x290, 0x291, 0x292, 0x293, 0x294, 0x295, 0x296, 0x297, + 0x298, 0x299, 0x29a, 0x29b, 0x29c, 0x29d, 0x29e, 0x29f, + 0x2a0, 0x2a1, 0x2a2, 0x2a3, 0x2a4, 0x2a5, 0x2a6, 0x2a7, + 0x2a8, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ad, 0x2ae, 0x2af, + 0x2b0, 0x2b1, 0x2b2, 0x2b3, 0x2b4, 0x2b5, 0x2b6, 0x2b7, + 0x2b8, 0x2b9, 0x2ba, 0x2bb, 0x2bc, 0x2bd, 0x2be, 0x2bf, + 0x2c0, 0x2c1, 0x2c2, 0x2c3, 0x2c4, 0x2c5, 0x2c6, 0x2c7, + 0x2c8, 0x2c9, 0x2ca, 0x2cb, 0x2cc, 0x2cd, 0x2ce, 0x2cf, + 0x2d0, 0x2d1, 0x2d2, 0x2d3, 0x2d4, 0x2d5, 0x2d6, 0x2d7, + 0x2d8, 0x2d9, 0x2da, 0x2db, 0x2dc, 0x2dd, 0x2de, 0x2df, + 0x2e0, 0x2e1, 0x2e2, 0x2e3, 0x2e4, 0x2e5, 0x2e6, 0x2e7, + 0x2e8, 0x2e9, 0x2ea, 0x2eb, 0x2ec, 0x2ed, 0x2ee, 0x2ef, + 0x2f0, 0x2f1, 0x2f2, 0x2f3, 0x2f4, 0x2f5, 0x2f6, 0x2f7, + 0x2f8, 0x2f9, 0x2fa, 0x2fb, 0x2fc, 0x2fd, 0x2fe, 0x2ff, +}; + +static const uint16_t elf_zlib_default_dist_table[0x100] = +{ + 0x800, 0x810, 0x808, 0x818, 0x804, 0x814, 0x80c, 0x81c, + 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, 0x81e, + 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, + 0x803, 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, + 0x800, 0x810, 0x808, 0x818, 0x804, 0x814, 0x80c, 0x81c, + 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, 0x81e, + 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, + 0x803, 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, + 0x800, 0x810, 0x808, 0x818, 0x804, 0x814, 0x80c, 0x81c, + 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, 0x81e, + 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, + 0x803, 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, + 0x800, 0x810, 0x808, 0x818, 0x804, 0x814, 0x80c, 0x81c, + 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, 0x81e, + 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, + 0x803, 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, + 0x800, 0x810, 0x808, 0x818, 0x804, 0x814, 0x80c, 0x81c, + 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, 0x81e, + 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, + 0x803, 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, + 0x800, 0x810, 0x808, 0x818, 0x804, 0x814, 0x80c, 0x81c, + 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, 0x81e, + 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, + 0x803, 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, + 0x800, 0x810, 0x808, 0x818, 0x804, 0x814, 0x80c, 0x81c, + 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, 0x81e, + 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, + 0x803, 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, + 0x800, 0x810, 0x808, 0x818, 0x804, 0x814, 0x80c, 0x81c, + 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, 0x81e, + 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, + 0x803, 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, +}; + +/* Inflate a zlib stream from PIN/SIN to POUT/SOUT. Return 1 on + success, 0 on some error parsing the stream. */ + +static int +elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, + unsigned char *pout, size_t sout) +{ + unsigned char *porigout; + const unsigned char *pinend; + unsigned char *poutend; + + /* We can apparently see multiple zlib streams concatenated + together, so keep going as long as there is something to read. + The last 4 bytes are the checksum. */ + porigout = pout; + pinend = pin + sin; + poutend = pout + sout; + while ((pinend - pin) > 4) + { + uint64_t val; + unsigned int bits; + int last; + + /* Read the two byte zlib header. */ + + if (unlikely ((pin[0] & 0xf) != 8)) /* 8 is zlib encoding. */ + { + /* Unknown compression method. */ + elf_zlib_failed (); + return 0; + } + if (unlikely ((pin[0] >> 4) > 7)) + { + /* Window size too large. Other than this check, we don't + care about the window size. */ + elf_zlib_failed (); + return 0; + } + if (unlikely ((pin[1] & 0x20) != 0)) + { + /* Stream expects a predefined dictionary, but we have no + dictionary. */ + elf_zlib_failed (); + return 0; + } + val = (pin[0] << 8) | pin[1]; + if (unlikely (val % 31 != 0)) + { + /* Header check failure. */ + elf_zlib_failed (); + return 0; + } + pin += 2; + + /* Align PIN to a 32-bit boundary. */ + + val = 0; + bits = 0; + while ((((uintptr_t) pin) & 3) != 0) + { + val |= (uint64_t)*pin << bits; + bits += 8; + ++pin; + } + + /* Read blocks until one is marked last. */ + + last = 0; + + while (!last) + { + unsigned int type; + const uint16_t *tlit; + const uint16_t *tdist; + + if (!elf_zlib_fetch (&pin, pinend, &val, &bits)) + return 0; + + last = val & 1; + type = (val >> 1) & 3; + val >>= 3; + bits -= 3; + + if (unlikely (type == 3)) + { + /* Invalid block type. */ + elf_zlib_failed (); + return 0; + } + + if (type == 0) + { + uint16_t len; + uint16_t lenc; + + /* An uncompressed block. */ + + /* If we've read ahead more than a byte, back up. */ + while (bits > 8) + { + --pin; + bits -= 8; + } + + val = 0; + bits = 0; + if (unlikely ((pinend - pin) < 4)) + { + /* Missing length. */ + elf_zlib_failed (); + return 0; + } + len = pin[0] | (pin[1] << 8); + lenc = pin[2] | (pin[3] << 8); + pin += 4; + lenc = ~lenc; + if (unlikely (len != lenc)) + { + /* Corrupt data. */ + elf_zlib_failed (); + return 0; + } + if (unlikely (len > (unsigned int) (pinend - pin) + || len > (unsigned int) (poutend - pout))) + { + /* Not enough space in buffers. */ + elf_zlib_failed (); + return 0; + } + memcpy (pout, pin, len); + pout += len; + pin += len; + + /* Align PIN. */ + while ((((uintptr_t) pin) & 3) != 0) + { + val |= (uint64_t)*pin << bits; + bits += 8; + ++pin; + } + + /* Go around to read the next block. */ + continue; + } + + if (type == 1) + { + tlit = elf_zlib_default_table; + tdist = elf_zlib_default_dist_table; + } + else + { + unsigned int nlit; + unsigned int ndist; + unsigned int nclen; + unsigned char codebits[19]; + unsigned char *plenbase; + unsigned char *plen; + unsigned char *plenend; + + /* Read a Huffman encoding table. The various magic + numbers here are from RFC 1951. */ + + if (!elf_zlib_fetch (&pin, pinend, &val, &bits)) + return 0; + + nlit = (val & 0x1f) + 257; + val >>= 5; + ndist = (val & 0x1f) + 1; + val >>= 5; + nclen = (val & 0xf) + 4; + val >>= 4; + bits -= 14; + if (unlikely (nlit > 286 || ndist > 30)) + { + /* Values out of range. */ + elf_zlib_failed (); + return 0; + } + + /* Read and build the table used to compress the + literal, length, and distance codes. */ + + memset(&codebits[0], 0, 19); + + /* There are always at least 4 elements in the + table. */ + + if (!elf_zlib_fetch (&pin, pinend, &val, &bits)) + return 0; + + codebits[16] = val & 7; + codebits[17] = (val >> 3) & 7; + codebits[18] = (val >> 6) & 7; + codebits[0] = (val >> 9) & 7; + val >>= 12; + bits -= 12; + + if (nclen == 4) + goto codebitsdone; + + codebits[8] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 5) + goto codebitsdone; + + if (!elf_zlib_fetch (&pin, pinend, &val, &bits)) + return 0; + + codebits[7] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 6) + goto codebitsdone; + + codebits[9] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 7) + goto codebitsdone; + + codebits[6] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 8) + goto codebitsdone; + + codebits[10] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 9) + goto codebitsdone; + + codebits[5] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 10) + goto codebitsdone; + + if (!elf_zlib_fetch (&pin, pinend, &val, &bits)) + return 0; + + codebits[11] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 11) + goto codebitsdone; + + codebits[4] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 12) + goto codebitsdone; + + codebits[12] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 13) + goto codebitsdone; + + codebits[3] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 14) + goto codebitsdone; + + codebits[13] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 15) + goto codebitsdone; + + if (!elf_zlib_fetch (&pin, pinend, &val, &bits)) + return 0; + + codebits[2] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 16) + goto codebitsdone; + + codebits[14] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 17) + goto codebitsdone; + + codebits[1] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 18) + goto codebitsdone; + + codebits[15] = val & 7; + val >>= 3; + bits -= 3; + + codebitsdone: + + if (!elf_zlib_inflate_table (codebits, 19, zdebug_table, + zdebug_table)) + return 0; + + /* Read the compressed bit lengths of the literal, + length, and distance codes. We have allocated space + at the end of zdebug_table to hold them. */ + + plenbase = (((unsigned char *) zdebug_table) + + ZDEBUG_TABLE_CODELEN_OFFSET); + plen = plenbase; + plenend = plen + nlit + ndist; + while (plen < plenend) + { + uint16_t t; + unsigned int b; + uint16_t v; + + if (!elf_zlib_fetch (&pin, pinend, &val, &bits)) + return 0; + + t = zdebug_table[val & 0xff]; + + /* The compression here uses bit lengths up to 7, so + a secondary table is never necessary. */ + if (unlikely ((t & (1U << HUFFMAN_SECONDARY_SHIFT)) != 0)) + { + elf_zlib_failed (); + return 0; + } + + b = (t >> HUFFMAN_BITS_SHIFT) & HUFFMAN_BITS_MASK; + val >>= b + 1; + bits -= b + 1; + + v = t & HUFFMAN_VALUE_MASK; + if (v < 16) + *plen++ = v; + else if (v == 16) + { + unsigned int c; + unsigned int prev; + + /* Copy previous entry 3 to 6 times. */ + + if (unlikely (plen == plenbase)) + { + elf_zlib_failed (); + return 0; + } + + /* We used up to 7 bits since the last + elf_zlib_fetch, so we have at least 8 bits + available here. */ + + c = 3 + (val & 0x3); + val >>= 2; + bits -= 2; + if (unlikely ((unsigned int) (plenend - plen) < c)) + { + elf_zlib_failed (); + return 0; + } + + prev = plen[-1]; + switch (c) + { + case 6: + *plen++ = prev; + /* fallthrough */ + case 5: + *plen++ = prev; + /* fallthrough */ + case 4: + *plen++ = prev; + } + *plen++ = prev; + *plen++ = prev; + *plen++ = prev; + } + else if (v == 17) + { + unsigned int c; + + /* Store zero 3 to 10 times. */ + + /* We used up to 7 bits since the last + elf_zlib_fetch, so we have at least 8 bits + available here. */ + + c = 3 + (val & 0x7); + val >>= 3; + bits -= 3; + if (unlikely ((unsigned int) (plenend - plen) < c)) + { + elf_zlib_failed (); + return 0; + } + + switch (c) + { + case 10: + *plen++ = 0; + /* fallthrough */ + case 9: + *plen++ = 0; + /* fallthrough */ + case 8: + *plen++ = 0; + /* fallthrough */ + case 7: + *plen++ = 0; + /* fallthrough */ + case 6: + *plen++ = 0; + /* fallthrough */ + case 5: + *plen++ = 0; + /* fallthrough */ + case 4: + *plen++ = 0; + } + *plen++ = 0; + *plen++ = 0; + *plen++ = 0; + } + else if (v == 18) + { + unsigned int c; + + /* Store zero 11 to 138 times. */ + + /* We used up to 7 bits since the last + elf_zlib_fetch, so we have at least 8 bits + available here. */ + + c = 11 + (val & 0x7f); + val >>= 7; + bits -= 7; + if (unlikely ((unsigned int) (plenend - plen) < c)) + { + elf_zlib_failed (); + return 0; + } + + memset (plen, 0, c); + plen += c; + } + else + { + elf_zlib_failed (); + return 0; + } + } + + /* Make sure that the stop code can appear. */ + + plen = plenbase; + if (unlikely (plen[256] == 0)) + { + elf_zlib_failed (); + return 0; + } + + /* Build the decompression tables. */ + + if (!elf_zlib_inflate_table (plen, nlit, zdebug_table, + zdebug_table)) + return 0; + if (!elf_zlib_inflate_table (plen + nlit, ndist, zdebug_table, + zdebug_table + HUFFMAN_TABLE_SIZE)) + return 0; + tlit = zdebug_table; + tdist = zdebug_table + HUFFMAN_TABLE_SIZE; + } + + /* Inflate values until the end of the block. This is the + main loop of the inflation code. */ + + while (1) + { + uint16_t t; + unsigned int b; + uint16_t v; + unsigned int lit; + + if (!elf_zlib_fetch (&pin, pinend, &val, &bits)) + return 0; + + t = tlit[val & 0xff]; + b = (t >> HUFFMAN_BITS_SHIFT) & HUFFMAN_BITS_MASK; + v = t & HUFFMAN_VALUE_MASK; + + if ((t & (1U << HUFFMAN_SECONDARY_SHIFT)) == 0) + { + lit = v; + val >>= b + 1; + bits -= b + 1; + } + else + { + t = tlit[v + 0x100 + ((val >> 8) & ((1U << b) - 1))]; + b = (t >> HUFFMAN_BITS_SHIFT) & HUFFMAN_BITS_MASK; + lit = t & HUFFMAN_VALUE_MASK; + val >>= b + 8; + bits -= b + 8; + } + + if (lit < 256) + { + if (unlikely (pout == poutend)) + { + elf_zlib_failed (); + return 0; + } + + *pout++ = lit; + + /* We will need to write the next byte soon. We ask + for high temporal locality because we will write + to the whole cache line soon. */ + __builtin_prefetch (pout, 1, 3); + } + else if (lit == 256) + { + /* The end of the block. */ + break; + } + else + { + unsigned int dist; + unsigned int len; + + /* Convert lit into a length. */ + + if (lit < 265) + len = lit - 257 + 3; + else if (lit == 285) + len = 258; + else if (unlikely (lit > 285)) + { + elf_zlib_failed (); + return 0; + } + else + { + unsigned int extra; + + if (!elf_zlib_fetch (&pin, pinend, &val, &bits)) + return 0; + + /* This is an expression for the table of length + codes in RFC 1951 3.2.5. */ + lit -= 265; + extra = (lit >> 2) + 1; + len = (lit & 3) << extra; + len += 11; + len += ((1U << (extra - 1)) - 1) << 3; + len += val & ((1U << extra) - 1); + val >>= extra; + bits -= extra; + } + + if (!elf_zlib_fetch (&pin, pinend, &val, &bits)) + return 0; + + t = tdist[val & 0xff]; + b = (t >> HUFFMAN_BITS_SHIFT) & HUFFMAN_BITS_MASK; + v = t & HUFFMAN_VALUE_MASK; + + if ((t & (1U << HUFFMAN_SECONDARY_SHIFT)) == 0) + { + dist = v; + val >>= b + 1; + bits -= b + 1; + } + else + { + t = tdist[v + 0x100 + ((val >> 8) & ((1U << b) - 1))]; + b = (t >> HUFFMAN_BITS_SHIFT) & HUFFMAN_BITS_MASK; + dist = t & HUFFMAN_VALUE_MASK; + val >>= b + 8; + bits -= b + 8; + } + + /* Convert dist to a distance. */ + + if (dist == 0) + { + /* A distance of 1. A common case, meaning + repeat the last character LEN times. */ + + if (unlikely (pout == porigout)) + { + elf_zlib_failed (); + return 0; + } + + if (unlikely ((unsigned int) (poutend - pout) < len)) + { + elf_zlib_failed (); + return 0; + } + + memset (pout, pout[-1], len); + pout += len; + } + else if (unlikely (dist > 29)) + { + elf_zlib_failed (); + return 0; + } + else + { + if (dist < 4) + dist = dist + 1; + else + { + unsigned int extra; + + if (!elf_zlib_fetch (&pin, pinend, &val, &bits)) + return 0; + + /* This is an expression for the table of + distance codes in RFC 1951 3.2.5. */ + dist -= 4; + extra = (dist >> 1) + 1; + dist = (dist & 1) << extra; + dist += 5; + dist += ((1U << (extra - 1)) - 1) << 2; + dist += val & ((1U << extra) - 1); + val >>= extra; + bits -= extra; + } + + /* Go back dist bytes, and copy len bytes from + there. */ + + if (unlikely ((unsigned int) (pout - porigout) < dist)) + { + elf_zlib_failed (); + return 0; + } + + if (unlikely ((unsigned int) (poutend - pout) < len)) + { + elf_zlib_failed (); + return 0; + } + + if (dist >= len) + { + memcpy (pout, pout - dist, len); + pout += len; + } + else + { + while (len > 0) + { + unsigned int copy; + + copy = len < dist ? len : dist; + memcpy (pout, pout - dist, copy); + len -= copy; + pout += copy; + } + } + } + } + } + } + } + + /* We should have filled the output buffer. */ + if (unlikely (pout != poutend)) + { + elf_zlib_failed (); + return 0; + } + + return 1; +} + +/* Verify the zlib checksum. The checksum is in the 4 bytes at + CHECKBYTES, and the uncompressed data is at UNCOMPRESSED / + UNCOMPRESSED_SIZE. Returns 1 on success, 0 on failure. */ + +static int +elf_zlib_verify_checksum (const unsigned char *checkbytes, + const unsigned char *uncompressed, + size_t uncompressed_size) +{ + unsigned int i; + unsigned int cksum; + const unsigned char *p; + uint32_t s1; + uint32_t s2; + size_t hsz; + + cksum = 0; + for (i = 0; i < 4; i++) + cksum = (cksum << 8) | checkbytes[i]; + + s1 = 1; + s2 = 0; + + /* Minimize modulo operations. */ + + p = uncompressed; + hsz = uncompressed_size; + while (hsz >= 5552) + { + for (i = 0; i < 5552; i += 16) + { + /* Manually unroll loop 16 times. */ + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + } + hsz -= 5552; + s1 %= 65521; + s2 %= 65521; + } + + while (hsz >= 16) + { + /* Manually unroll loop 16 times. */ + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + + hsz -= 16; + } + + for (i = 0; i < hsz; ++i) + { + s1 = s1 + *p++; + s2 = s2 + s1; + } + + s1 %= 65521; + s2 %= 65521; + + if (unlikely ((s2 << 16) + s1 != cksum)) + { + elf_zlib_failed (); + return 0; + } + + return 1; +} + +/* Inflate a zlib stream from PIN/SIN to POUT/SOUT, and verify the + checksum. Return 1 on success, 0 on error. */ + +static int +elf_zlib_inflate_and_verify (const unsigned char *pin, size_t sin, + uint16_t *zdebug_table, unsigned char *pout, + size_t sout) +{ + if (!elf_zlib_inflate (pin, sin, zdebug_table, pout, sout)) + return 0; + if (!elf_zlib_verify_checksum (pin + sin - 4, pout, sout)) + return 0; + return 1; +} + +/* Uncompress the old compressed debug format, the one emitted by + --compress-debug-sections=zlib-gnu. The compressed data is in + COMPRESSED / COMPRESSED_SIZE, and the function writes to + *UNCOMPRESSED / *UNCOMPRESSED_SIZE. ZDEBUG_TABLE is work space to + hold Huffman tables. Returns 0 on error, 1 on successful + decompression or if something goes wrong. In general we try to + carry on, by returning 1, even if we can't decompress. */ + +static int +elf_uncompress_zdebug (struct backtrace_state *state, + const unsigned char *compressed, size_t compressed_size, + uint16_t *zdebug_table, + backtrace_error_callback error_callback, void *data, + unsigned char **uncompressed, size_t *uncompressed_size) +{ + size_t sz; + size_t i; + unsigned char *po; + + *uncompressed = NULL; + *uncompressed_size = 0; + + /* The format starts with the four bytes ZLIB, followed by the 8 + byte length of the uncompressed data in big-endian order, + followed by a zlib stream. */ + + if (compressed_size < 12 || memcmp (compressed, "ZLIB", 4) != 0) + return 1; + + sz = 0; + for (i = 0; i < 8; i++) + sz = (sz << 8) | compressed[i + 4]; + + if (*uncompressed != NULL && *uncompressed_size >= sz) + po = *uncompressed; + else + { + po = (unsigned char *) backtrace_alloc (state, sz, error_callback, data); + if (po == NULL) + return 0; + } + + if (!elf_zlib_inflate_and_verify (compressed + 12, compressed_size - 12, + zdebug_table, po, sz)) + return 1; + + *uncompressed = po; + *uncompressed_size = sz; + + return 1; +} + +/* Uncompress the new compressed debug format, the official standard + ELF approach emitted by --compress-debug-sections=zlib-gabi. The + compressed data is in COMPRESSED / COMPRESSED_SIZE, and the + function writes to *UNCOMPRESSED / *UNCOMPRESSED_SIZE. + ZDEBUG_TABLE is work space as for elf_uncompress_zdebug. Returns 0 + on error, 1 on successful decompression or if something goes wrong. + In general we try to carry on, by returning 1, even if we can't + decompress. */ + +static int +elf_uncompress_chdr (struct backtrace_state *state, + const unsigned char *compressed, size_t compressed_size, + uint16_t *zdebug_table, + backtrace_error_callback error_callback, void *data, + unsigned char **uncompressed, size_t *uncompressed_size) +{ + const b_elf_chdr *chdr; + unsigned char *po; + + *uncompressed = NULL; + *uncompressed_size = 0; + + /* The format starts with an ELF compression header. */ + if (compressed_size < sizeof (b_elf_chdr)) + return 1; + + chdr = (const b_elf_chdr *) compressed; + + if (chdr->ch_type != ELFCOMPRESS_ZLIB) + { + /* Unsupported compression algorithm. */ + return 1; + } + + if (*uncompressed != NULL && *uncompressed_size >= chdr->ch_size) + po = *uncompressed; + else + { + po = (unsigned char *) backtrace_alloc (state, chdr->ch_size, + error_callback, data); + if (po == NULL) + return 0; + } + + if (!elf_zlib_inflate_and_verify (compressed + sizeof (b_elf_chdr), + compressed_size - sizeof (b_elf_chdr), + zdebug_table, po, chdr->ch_size)) + return 1; + + *uncompressed = po; + *uncompressed_size = chdr->ch_size; + + return 1; +} + +/* This function is a hook for testing the zlib support. It is only + used by tests. */ + +int +backtrace_uncompress_zdebug (struct backtrace_state *state, + const unsigned char *compressed, + size_t compressed_size, + backtrace_error_callback error_callback, + void *data, unsigned char **uncompressed, + size_t *uncompressed_size) +{ + uint16_t *zdebug_table; + int ret; + + zdebug_table = ((uint16_t *) backtrace_alloc (state, ZDEBUG_TABLE_SIZE, + error_callback, data)); + if (zdebug_table == NULL) + return 0; + ret = elf_uncompress_zdebug (state, compressed, compressed_size, + zdebug_table, error_callback, data, + uncompressed, uncompressed_size); + backtrace_free (state, zdebug_table, ZDEBUG_TABLE_SIZE, + error_callback, data); + return ret; +} + /* Add the backtrace data for one ELF file. Returns 1 on success, 0 on failure (in both cases descriptor is closed) or -1 if exe is non-zero and the ELF file is ET_DYN, which tells the caller that @@ -1011,6 +2631,8 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, off_t max_offset; struct backtrace_view debug_view; int debug_view_valid; + unsigned int using_debug_view; + uint16_t *zdebug_table; *found_sym = 0; *found_dwarf = 0; @@ -1174,6 +2796,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, { sections[j].offset = shdr->sh_offset; sections[j].size = shdr->sh_size; + sections[j].compressed = (shdr->sh_flags & SHF_COMPRESSED) != 0; break; } } @@ -1284,8 +2907,6 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, elf_add_syminfo_data (state, sdata); } - /* FIXME: Need to handle compressed debug sections. */ - backtrace_release_view (state, &shdrs_view, error_callback, data); shdrs_view_valid = 0; backtrace_release_view (state, &names_view, error_callback, data); @@ -1373,13 +2994,94 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, goto fail; descriptor = -1; + using_debug_view = 0; for (i = 0; i < (int) DEBUG_MAX; ++i) { if (sections[i].size == 0) sections[i].data = NULL; else - sections[i].data = ((const unsigned char *) debug_view.data - + (sections[i].offset - min_offset)); + { + sections[i].data = ((const unsigned char *) debug_view.data + + (sections[i].offset - min_offset)); + if (i < ZDEBUG_INFO) + ++using_debug_view; + } + } + + /* Uncompress the old format (--compress-debug-sections=zlib-gnu). */ + + zdebug_table = NULL; + for (i = 0; i < ZDEBUG_INFO; ++i) + { + struct debug_section_info *pz; + + pz = §ions[i + ZDEBUG_INFO - DEBUG_INFO]; + if (sections[i].size == 0 && pz->size > 0) + { + unsigned char *uncompressed_data; + size_t uncompressed_size; + + if (zdebug_table == NULL) + { + zdebug_table = ((uint16_t *) + backtrace_alloc (state, ZDEBUG_TABLE_SIZE, + error_callback, data)); + if (zdebug_table == NULL) + goto fail; + } + + uncompressed_data = NULL; + uncompressed_size = 0; + if (!elf_uncompress_zdebug (state, pz->data, pz->size, zdebug_table, + error_callback, data, + &uncompressed_data, &uncompressed_size)) + goto fail; + sections[i].data = uncompressed_data; + sections[i].size = uncompressed_size; + sections[i].compressed = 0; + } + } + + /* Uncompress the official ELF format + (--compress-debug-sections=zlib-gabi). */ + for (i = 0; i < ZDEBUG_INFO; ++i) + { + unsigned char *uncompressed_data; + size_t uncompressed_size; + + if (sections[i].size == 0 || !sections[i].compressed) + continue; + + if (zdebug_table == NULL) + { + zdebug_table = ((uint16_t *) + backtrace_alloc (state, ZDEBUG_TABLE_SIZE, + error_callback, data)); + if (zdebug_table == NULL) + goto fail; + } + + uncompressed_data = NULL; + uncompressed_size = 0; + if (!elf_uncompress_chdr (state, sections[i].data, sections[i].size, + zdebug_table, error_callback, data, + &uncompressed_data, &uncompressed_size)) + goto fail; + sections[i].data = uncompressed_data; + sections[i].size = uncompressed_size; + sections[i].compressed = 0; + + --using_debug_view; + } + + if (zdebug_table != NULL) + backtrace_free (state, zdebug_table, ZDEBUG_TABLE_SIZE, + error_callback, data); + + if (debug_view_valid && using_debug_view == 0) + { + backtrace_release_view (state, &debug_view, error_callback, data); + debug_view_valid = 0; } if (!backtrace_dwarf_add (state, base_address, diff --git a/libbacktrace/internal.h b/libbacktrace/internal.h index 0a35921..8ae0be5 100644 --- a/libbacktrace/internal.h +++ b/libbacktrace/internal.h @@ -292,4 +292,13 @@ extern int backtrace_dwarf_add (struct backtrace_state *state, backtrace_error_callback error_callback, void *data, fileline *fileline_fn); +/* A test-only hook for elf_uncompress_zdebug. */ + +extern int backtrace_uncompress_zdebug (struct backtrace_state *, + const unsigned char *compressed, + size_t compressed_size, + backtrace_error_callback, void *data, + unsigned char **uncompressed, + size_t *uncompressed_size); + #endif diff --git a/libbacktrace/xcoff.c b/libbacktrace/xcoff.c index f752391..f84bf26 100644 --- a/libbacktrace/xcoff.c +++ b/libbacktrace/xcoff.c @@ -124,9 +124,16 @@ typedef struct { #endif /* BACKTRACE_XCOFF_SIZE != 32 */ +#define STYP_DWARF 0x10 /* DWARF debugging section. */ #define STYP_TEXT 0x20 /* Executable text (code) section. */ #define STYP_OVRFLO 0x8000 /* Line-number field overflow section. */ +#define SSUBTYP_DWINFO 0x10000 /* DWARF info section. */ +#define SSUBTYP_DWLINE 0x20000 /* DWARF line-number section. */ +#define SSUBTYP_DWARNGE 0x50000 /* DWARF aranges section. */ +#define SSUBTYP_DWABREV 0x60000 /* DWARF abbreviation section. */ +#define SSUBTYP_DWSTR 0x70000 /* DWARF strings section. */ + /* XCOFF symbol. */ #define SYMNMLEN 8 @@ -367,6 +374,29 @@ struct xcoff_fileline_data struct xcoff_line_vector vec; }; +/* An index of DWARF sections we care about. */ + +enum dwarf_section +{ + DWSECT_INFO, + DWSECT_LINE, + DWSECT_ABBREV, + DWSECT_RANGES, + DWSECT_STR, + DWSECT_MAX +}; + +/* Information we gather for the DWARF sections we care about. */ + +struct dwsect_info +{ + /* Section file offset. */ + off_t offset; + /* Section size. */ + size_t size; + /* Section contents, after read from file. */ + const unsigned char *data; +}; /* A dummy callback function used when we can't find any debug info. */ @@ -1056,12 +1086,16 @@ xcoff_add (struct backtrace_state *state, int descriptor, off_t offset, struct backtrace_view linenos_view; struct backtrace_view syms_view; struct backtrace_view str_view; + struct backtrace_view dwarf_view; b_xcoff_filhdr fhdr; const b_xcoff_scnhdr *sects; const b_xcoff_scnhdr *stext; uint64_t lnnoptr; uint32_t nlnno; off_t str_off; + off_t min_offset; + off_t max_offset; + struct dwsect_info dwsect[DWSECT_MAX]; size_t sects_size; size_t syms_size; int32_t str_size; @@ -1069,6 +1103,7 @@ xcoff_add (struct backtrace_state *state, int descriptor, off_t offset, int linenos_view_valid; int syms_view_valid; int str_view_valid; + int dwarf_view_valid; int magic_ok; int i; @@ -1078,6 +1113,9 @@ xcoff_add (struct backtrace_state *state, int descriptor, off_t offset, linenos_view_valid = 0; syms_view_valid = 0; str_view_valid = 0; + dwarf_view_valid = 0; + + str_size = 0; /* Map the XCOFF file header. */ if (!backtrace_get_view (state, descriptor, offset, sizeof (b_xcoff_filhdr), @@ -1092,7 +1130,7 @@ xcoff_add (struct backtrace_state *state, int descriptor, off_t offset, if (!magic_ok) { if (exe) - error_callback (data, "executable file is not XCOFF", 0); + error_callback (data, "executable file is not XCOFF", 0); goto fail; } @@ -1114,8 +1152,8 @@ xcoff_add (struct backtrace_state *state, int descriptor, off_t offset, /* FIXME: assumes only one .text section. */ for (i = 0; i < fhdr.f_nscns; ++i) - if ((sects[i].s_flags & 0xffff) == STYP_TEXT) - break; + if ((sects[i].s_flags & 0xffff) == STYP_TEXT) + break; if (i == fhdr.f_nscns) goto fail; @@ -1134,12 +1172,12 @@ xcoff_add (struct backtrace_state *state, int descriptor, off_t offset, /* Find the matching .ovrflo section. */ for (i = 0; i < fhdr.f_nscns; ++i) { - if (((sects[i].s_flags & 0xffff) == STYP_OVRFLO) - && sects[i].s_nlnno == sntext) - { - nlnno = sects[i].s_vaddr; - break; - } + if (((sects[i].s_flags & 0xffff) == STYP_OVRFLO) + && sects[i].s_nlnno == sntext) + { + nlnno = sects[i].s_vaddr; + break; + } } } #endif @@ -1194,9 +1232,91 @@ xcoff_add (struct backtrace_state *state, int descriptor, off_t offset, xcoff_add_syminfo_data (state, sdata); } - /* Read the line number entries. */ + /* Read all the DWARF sections in a single view, since they are + probably adjacent in the file. We never release this view. */ + + min_offset = 0; + max_offset = 0; + memset (dwsect, 0, sizeof dwsect); + for (i = 0; i < fhdr.f_nscns; ++i) + { + off_t end; + int idx; + + if ((sects[i].s_flags & 0xffff) != STYP_DWARF + || sects[i].s_size == 0) + continue; + /* Map DWARF section to array index. */ + switch (sects[i].s_flags & 0xffff0000) + { + case SSUBTYP_DWINFO: + idx = DWSECT_INFO; + break; + case SSUBTYP_DWLINE: + idx = DWSECT_LINE; + break; + case SSUBTYP_DWABREV: + idx = DWSECT_ABBREV; + break; + case SSUBTYP_DWARNGE: + idx = DWSECT_RANGES; + break; + case SSUBTYP_DWSTR: + idx = DWSECT_STR; + break; + default: + continue; + } + if (min_offset == 0 || (off_t) sects[i].s_scnptr < min_offset) + min_offset = sects[i].s_scnptr; + end = sects[i].s_scnptr + sects[i].s_size; + if (end > max_offset) + max_offset = end; + dwsect[idx].offset = sects[i].s_scnptr; + dwsect[idx].size = sects[i].s_size; + } + if (min_offset != 0 && max_offset != 0) + { + if (!backtrace_get_view (state, descriptor, offset + min_offset, + max_offset - min_offset, + error_callback, data, &dwarf_view)) + goto fail; + dwarf_view_valid = 1; + + for (i = 0; i < (int) DWSECT_MAX; ++i) + { + if (dwsect[i].offset == 0) + dwsect[i].data = NULL; + else + dwsect[i].data = ((const unsigned char *) dwarf_view.data + + (dwsect[i].offset - min_offset)); + } + + if (!backtrace_dwarf_add (state, 0, + dwsect[DWSECT_INFO].data, + dwsect[DWSECT_INFO].size, +#if BACKTRACE_XCOFF_SIZE == 32 + /* XXX workaround for broken lineoff */ + dwsect[DWSECT_LINE].data - 4, +#else + /* XXX workaround for broken lineoff */ + dwsect[DWSECT_LINE].data - 12, +#endif + dwsect[DWSECT_LINE].size, + dwsect[DWSECT_ABBREV].data, + dwsect[DWSECT_ABBREV].size, + dwsect[DWSECT_RANGES].data, + dwsect[DWSECT_RANGES].size, + dwsect[DWSECT_STR].data, + dwsect[DWSECT_STR].size, + 1, /* big endian */ + error_callback, data, fileline_fn)) + goto fail; + } + + /* Read the XCOFF line number entries if DWARF sections not found. */ - if (fhdr.f_symptr != 0 && lnnoptr != 0) + if (!dwarf_view_valid && fhdr.f_symptr != 0 && lnnoptr != 0) { size_t linenos_size = (size_t) nlnno * LINESZ; @@ -1239,6 +1359,8 @@ xcoff_add (struct backtrace_state *state, int descriptor, off_t offset, backtrace_release_view (state, &syms_view, error_callback, data); if (linenos_view_valid) backtrace_release_view (state, &linenos_view, error_callback, data); + if (dwarf_view_valid) + backtrace_release_view (state, &dwarf_view, error_callback, data); if (descriptor != -1 && offset == 0) backtrace_close (descriptor, error_callback, data); return 0; diff --git a/libbacktrace/ztest.c b/libbacktrace/ztest.c new file mode 100644 index 0000000..a3d105d --- /dev/null +++ b/libbacktrace/ztest.c @@ -0,0 +1,537 @@ +/* ztest.c -- Test for libbacktrace inflate code. + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +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. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +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. */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ZLIB +#include +#endif + +#include "backtrace.h" +#include "backtrace-supported.h" + +#include "internal.h" +#include "testlib.h" + +#ifndef HAVE_CLOCK_GETTIME + +typedef int xclockid_t; + +static int +xclock_gettime (xclockid_t id ATTRIBUTE_UNUSED, + struct timespec *ts ATTRIBUTE_UNUSED) +{ + errno = EINVAL; + return -1; +} + +#define clockid_t xclockid_t +#define clock_gettime xclock_gettime +#undef CLOCK_REALTIME +#define CLOCK_REALTIME 0 + +#endif /* !defined(HAVE_CLOCK_GETTIME) */ + +#ifdef CLOCK_PROCESS_CPUTIME_ID +#define ZLIB_CLOCK_GETTIME_ARG CLOCK_PROCESS_CPUTIME_ID +#else +#define ZLIB_CLOCK_GETTIME_ARG CLOCK_REALTIME +#endif + +/* Some tests for the local zlib inflation code. */ + +struct zlib_test +{ + const char *name; + const char *uncompressed; + size_t uncompressed_len; + const char *compressed; + size_t compressed_len; +}; + +/* Error callback. */ + +static void +error_callback_compress (void *vdata, const char *msg, int errnum) +{ + fprintf (stderr, "%s", msg); + if (errnum > 0) + fprintf (stderr, ": %s", strerror (errnum)); + fprintf (stderr, "\n"); + exit (EXIT_FAILURE); +} + +static const struct zlib_test tests[] = +{ + { + "empty", + "", + 0, + "\x78\x9c\x03\x00\x00\x00\x00\x01", + 8, + }, + { + "hello", + "hello, world\n", + 0, + ("\x78\x9c\xca\x48\xcd\xc9\xc9\xd7\x51\x28\xcf" + "\x2f\xca\x49\xe1\x02\x04\x00\x00\xff\xff\x21\xe7\x04\x93"), + 25, + }, + { + "goodbye", + "goodbye, world", + 0, + ("\x78\x9c\x4b\xcf\xcf\x4f\x49\xaa" + "\x4c\xd5\x51\x28\xcf\x2f\xca\x49" + "\x01\x00\x28\xa5\x05\x5e"), + 22, + }, + { + "ranges", + ("\xcc\x11\x00\x00\x00\x00\x00\x00\xd5\x13\x00\x00\x00\x00\x00\x00" + "\x1c\x14\x00\x00\x00\x00\x00\x00\x72\x14\x00\x00\x00\x00\x00\x00" + "\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xfb\x12\x00\x00\x00\x00\x00\x00\x09\x13\x00\x00\x00\x00\x00\x00" + "\x0c\x13\x00\x00\x00\x00\x00\x00\xcb\x13\x00\x00\x00\x00\x00\x00" + "\x29\x14\x00\x00\x00\x00\x00\x00\x4e\x14\x00\x00\x00\x00\x00\x00" + "\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xfb\x12\x00\x00\x00\x00\x00\x00\x09\x13\x00\x00\x00\x00\x00\x00" + "\x67\x13\x00\x00\x00\x00\x00\x00\xcb\x13\x00\x00\x00\x00\x00\x00" + "\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x5f\x0b\x00\x00\x00\x00\x00\x00\x6c\x0b\x00\x00\x00\x00\x00\x00" + "\x7d\x0b\x00\x00\x00\x00\x00\x00\x7e\x0c\x00\x00\x00\x00\x00\x00" + "\x38\x0f\x00\x00\x00\x00\x00\x00\x5c\x0f\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x83\x0c\x00\x00\x00\x00\x00\x00\xfa\x0c\x00\x00\x00\x00\x00\x00" + "\xfd\x0d\x00\x00\x00\x00\x00\x00\xef\x0e\x00\x00\x00\x00\x00\x00" + "\x14\x0f\x00\x00\x00\x00\x00\x00\x38\x0f\x00\x00\x00\x00\x00\x00" + "\x9f\x0f\x00\x00\x00\x00\x00\x00\xac\x0f\x00\x00\x00\x00\x00\x00" + "\xdb\x0f\x00\x00\x00\x00\x00\x00\xff\x0f\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xfd\x0d\x00\x00\x00\x00\x00\x00\xd8\x0e\x00\x00\x00\x00\x00\x00" + "\x9f\x0f\x00\x00\x00\x00\x00\x00\xac\x0f\x00\x00\x00\x00\x00\x00" + "\xdb\x0f\x00\x00\x00\x00\x00\x00\xff\x0f\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xfa\x0c\x00\x00\x00\x00\x00\x00\xea\x0d\x00\x00\x00\x00\x00\x00" + "\xef\x0e\x00\x00\x00\x00\x00\x00\x14\x0f\x00\x00\x00\x00\x00\x00" + "\x5c\x0f\x00\x00\x00\x00\x00\x00\x9f\x0f\x00\x00\x00\x00\x00\x00" + "\xac\x0f\x00\x00\x00\x00\x00\x00\xdb\x0f\x00\x00\x00\x00\x00\x00" + "\xff\x0f\x00\x00\x00\x00\x00\x00\x2c\x10\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x60\x11\x00\x00\x00\x00\x00\x00\xd1\x16\x00\x00\x00\x00\x00\x00" + "\x40\x0b\x00\x00\x00\x00\x00\x00\x2c\x10\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x7a\x00\x00\x00\x00\x00\x00\x00\xb6\x00\x00\x00\x00\x00\x00\x00" + "\x9f\x01\x00\x00\x00\x00\x00\x00\xa7\x01\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x7a\x00\x00\x00\x00\x00\x00\x00\xa9\x00\x00\x00\x00\x00\x00\x00" + "\x9f\x01\x00\x00\x00\x00\x00\x00\xa7\x01\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), + 672, + ("\x78\x9c\x3b\x23\xc8\x00\x06\x57\x85\x21\xb4\x8c\x08\x84\x2e\x82" + "\xd2\x73\xa1\xf4\x55\x28\x8d\x0e\x7e\x0b\x41\x68\x4e\xa8\x7e\x1e" + "\x28\x7d\x1a\x4a\x6b\x42\xf5\xf9\x91\x69\x5e\x3a\x9a\x79\x84\xf4" + "\xc7\x73\x43\xe8\x1c\x28\x5d\x0b\xa5\xeb\x78\x20\xb4\x05\x3f\x84" + "\x8e\xe1\xc7\xae\xbf\x19\xaa\xee\x17\x94\xfe\xcb\x0b\xa1\xdf\xf3" + "\x41\x68\x11\x7e\x54\x73\xe6\x43\xe9\x35\x50\xfa\x36\x94\xfe\x8f" + "\xc3\x7c\x98\x79\x37\xf8\xc8\xd3\x0f\x73\xd7\x2b\x1c\xee\x8a\x21" + "\xd2\x5d\x3a\x02\xd8\xcd\x4f\x80\xa6\x87\x8b\x62\x10\xda\x81\x1b" + "\xbf\xfa\x2a\x28\xbd\x0d\x4a\xcf\x67\x84\xd0\xcb\x19\xf1\xab\x5f" + "\x49\xa4\x7a\x00\x48\x97\x29\xd4"), + 152, + } +}; + +/* Test the hand coded samples. */ + +static void +test_samples (struct backtrace_state *state) +{ + size_t i; + + for (i = 0; i < sizeof tests / sizeof tests[0]; ++i) + { + char *p; + size_t v; + size_t j; + unsigned char *uncompressed; + size_t uncompressed_len; + + p = malloc (12 + tests[i].compressed_len); + memcpy (p, "ZLIB", 4); + v = tests[i].uncompressed_len; + if (v == 0) + v = strlen (tests[i].uncompressed); + for (j = 0; j < 8; ++j) + p[j + 4] = (v >> ((7 - j) * 8)) & 0xff; + memcpy (p + 12, tests[i].compressed, tests[i].compressed_len); + uncompressed = NULL; + uncompressed_len = 0; + if (!backtrace_uncompress_zdebug (state, (unsigned char *) p, + tests[i].compressed_len + 12, + error_callback_compress, NULL, + &uncompressed, &uncompressed_len)) + { + fprintf (stderr, "test %s: uncompress failed\n", tests[i].name); + ++failures; + } + else + { + if (uncompressed_len != v) + { + fprintf (stderr, + "test %s: got uncompressed length %zu, want %zu\n", + tests[i].name, uncompressed_len, v); + ++failures; + } + else if (memcmp (tests[i].uncompressed, uncompressed, v) != 0) + { + size_t j; + + fprintf (stderr, "test %s: uncompressed data mismatch\n", + tests[i].name); + for (j = 0; j < v; ++j) + if (tests[i].uncompressed[j] != uncompressed[j]) + fprintf (stderr, " %zu: got %#x want %#x\n", j, + uncompressed[j], tests[i].uncompressed[j]); + ++failures; + } + else + printf ("PASS: inflate %s\n", tests[i].name); + + backtrace_free (state, uncompressed, uncompressed_len, + error_callback_compress, NULL); + } + } +} + +#ifdef HAVE_ZLIB + +/* Given a set of TRIALS timings, discard the lowest and highest + values and return the mean average of the rest. */ + +static size_t +average_time (const size_t *times, size_t trials) +{ + size_t imax; + size_t max; + size_t imin; + size_t min; + size_t i; + size_t sum; + + imin = 0; + imax = 0; + min = times[0]; + max = times[0]; + for (i = 1; i < trials; ++i) + { + if (times[i] < min) + { + imin = i; + min = times[i]; + } + if (times[i] > max) + { + imax = i; + max = times[i]; + } + } + + sum = 0; + for (i = 0; i < trials; ++i) + { + if (i != imax && i != imin) + sum += times[i]; + } + return sum / (trials - 2); +} + +#endif + +/* Test a larger text, if available. */ + +static void +test_large (struct backtrace_state *state) +{ +#ifdef HAVE_ZLIB + unsigned char *orig_buf; + size_t orig_bufsize; + size_t i; + char *compressed_buf; + size_t compressed_bufsize; + unsigned long compress_sizearg; + unsigned char *uncompressed_buf; + size_t uncompressed_bufsize; + int r; + clockid_t cid; + struct timespec ts1; + struct timespec ts2; + size_t ctime; + size_t ztime; + const size_t trials = 16; + size_t ctimes[16]; + size_t ztimes[16]; + static const char * const names[] = { + "Mark.Twain-Tom.Sawyer.txt", + "../libgo/go/compress/testdata/Mark.Twain-Tom.Sawyer.txt" + }; + + orig_buf = NULL; + orig_bufsize = 0; + uncompressed_buf = NULL; + compressed_buf = NULL; + + for (i = 0; i < sizeof names / sizeof names[0]; ++i) + { + size_t len; + char *namebuf; + FILE *e; + struct stat st; + char *rbuf; + size_t got; + + len = strlen (SRCDIR) + strlen (names[i]) + 2; + namebuf = malloc (len); + if (namebuf == NULL) + { + perror ("malloc"); + goto fail; + } + snprintf (namebuf, len, "%s/%s", SRCDIR, names[i]); + e = fopen (namebuf, "r"); + free (namebuf); + if (e == NULL) + continue; + if (fstat (fileno (e), &st) < 0) + { + perror ("fstat"); + fclose (e); + continue; + } + rbuf = malloc (st.st_size); + if (rbuf == NULL) + { + perror ("malloc"); + goto fail; + } + got = fread (rbuf, 1, st.st_size, e); + fclose (e); + if (got > 0) + { + orig_buf = rbuf; + orig_bufsize = got; + break; + } + free (rbuf); + } + + if (orig_buf == NULL) + { + /* We couldn't find an input file. */ + printf ("UNSUPPORTED: inflate large\n"); + return; + } + + compressed_bufsize = compressBound (orig_bufsize) + 12; + compressed_buf = malloc (compressed_bufsize); + if (compressed_buf == NULL) + { + perror ("malloc"); + goto fail; + } + + compress_sizearg = compressed_bufsize - 12; + r = compress (compressed_buf + 12, &compress_sizearg, + orig_buf, orig_bufsize); + if (r != Z_OK) + { + fprintf (stderr, "zlib compress failed: %d\n", r); + goto fail; + } + + compressed_bufsize = compress_sizearg + 12; + + /* Prepare the header that our library expects. */ + memcpy (compressed_buf, "ZLIB", 4); + for (i = 0; i < 8; ++i) + compressed_buf[i + 4] = (orig_bufsize >> ((7 - i) * 8)) & 0xff; + + uncompressed_buf = malloc (orig_bufsize); + if (uncompressed_buf == NULL) + { + perror ("malloc"); + goto fail; + } + uncompressed_bufsize = orig_bufsize; + + if (!backtrace_uncompress_zdebug (state, compressed_buf, compressed_bufsize, + error_callback_compress, NULL, + &uncompressed_buf, &uncompressed_bufsize)) + { + fprintf (stderr, "inflate large: backtrace_uncompress_zdebug failed\n"); + goto fail; + } + + if (uncompressed_bufsize != orig_bufsize) + { + fprintf (stderr, + "inflate large: got uncompressed length %zu, want %zu\n", + uncompressed_bufsize, orig_bufsize); + goto fail; + } + + if (memcmp (uncompressed_buf, orig_buf, uncompressed_bufsize) != 0) + { + fprintf (stderr, "inflate large: uncompressed data mismatch\n"); + goto fail; + } + + printf ("PASS: inflate large\n"); + + for (i = 0; i < trials; ++i) + { + unsigned long uncompress_sizearg; + + cid = ZLIB_CLOCK_GETTIME_ARG; + if (clock_gettime (cid, &ts1) < 0) + { + if (errno == EINVAL) + return; + perror ("clock_gettime"); + return; + } + + if (!backtrace_uncompress_zdebug (state, compressed_buf, + compressed_bufsize, + error_callback_compress, NULL, + &uncompressed_buf, + &uncompressed_bufsize)) + { + fprintf (stderr, + ("inflate large: " + "benchmark backtrace_uncompress_zdebug failed\n")); + return; + } + + if (clock_gettime (cid, &ts2) < 0) + { + perror ("clock_gettime"); + return; + } + + ctime = (ts2.tv_sec - ts1.tv_sec) * 1000000000; + ctime += ts2.tv_nsec - ts1.tv_nsec; + ctimes[i] = ctime; + + if (clock_gettime (cid, &ts1) < 0) + { + perror("clock_gettime"); + return; + } + + uncompress_sizearg = uncompressed_bufsize; + r = uncompress (uncompressed_buf, &uncompress_sizearg, + compressed_buf + 12, compressed_bufsize - 12); + + if (clock_gettime (cid, &ts2) < 0) + { + perror ("clock_gettime"); + return; + } + + if (r != Z_OK) + { + fprintf (stderr, + "inflate large: benchmark zlib uncompress failed: %d\n", + r); + return; + } + + ztime = (ts2.tv_sec - ts1.tv_sec) * 1000000000; + ztime += ts2.tv_nsec - ts1.tv_nsec; + ztimes[i] = ztime; + } + + /* Toss the highest and lowest times and average the rest. */ + ctime = average_time (ctimes, trials); + ztime = average_time (ztimes, trials); + + printf ("backtrace: %zu ns\n", ctime); + printf ("zlib : %zu ns\n", ztime); + printf ("ratio : %g\n", (double) ztime / (double) ctime); + + return; + + fail: + printf ("FAIL: inflate large\n"); + ++failures; + + if (orig_buf != NULL) + free (orig_buf); + if (compressed_buf != NULL) + free (compressed_buf); + if (uncompressed_buf != NULL) + free (uncompressed_buf); + +#else /* !HAVE_ZLIB */ + + printf ("UNSUPPORTED: inflate large\n"); + +#endif /* !HAVE_ZLIB */ +} + +int +main (int argc ATTRIBUTE_UNUSED, char **argv) +{ + struct backtrace_state *state; + + state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, + error_callback_create, NULL); + + test_samples (state); + test_large (state); + + exit (failures != 0 ? EXIT_FAILURE : EXIT_SUCCESS); +} -- 2.7.4 From a6ad3b6f23a910ac951461f251ea79be2644f703 Mon Sep 17 00:00:00 2001 From: Denis Khalikov Date: Thu, 14 Jun 2018 15:59:00 +0300 Subject: [PATCH 07/16] PR sanitizer/86090 libsanitizer/ * configure.ac: Check for lstat and readlink. * configure, config.h.in: Rebuild. Change-Id: I3c81a03c34828604f3be9db3906c4ecffafc360c --- libsanitizer/ChangeLog | 6 ++++++ libsanitizer/config.h.in | 6 ++++++ libsanitizer/configure | 2 +- libsanitizer/configure.ac | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog index a4cba4e..3578de6 100644 --- a/libsanitizer/ChangeLog +++ b/libsanitizer/ChangeLog @@ -1,3 +1,9 @@ +2018-06-13 Denis Khalikov + + PR sanitizer/86090 + * configure.ac: Check for lstat and readlink. + * configure, config.h.in: Rebuild. + 2016-11-09 Maxim Ostapenko * sanitizer_common/sanitizer_stacktrace.cc (GetCanonicFrame): Assume we diff --git a/libsanitizer/config.h.in b/libsanitizer/config.h.in index 7195840..f716c24 100644 --- a/libsanitizer/config.h.in +++ b/libsanitizer/config.h.in @@ -43,6 +43,12 @@ /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H +/* Define to 1 if you have the `lstat' function. */ +#undef HAVE_LSTAT + +/* Define to 1 if you have the `readlink' function. */ +#undef HAVE_READLINK + /* Define to 1 if you have the header file. */ #undef HAVE_RPC_XDR_H diff --git a/libsanitizer/configure b/libsanitizer/configure index 2861330..29253d8 100755 --- a/libsanitizer/configure +++ b/libsanitizer/configure @@ -15513,7 +15513,7 @@ fi # Check for functions needed. -for ac_func in clock_getres clock_gettime clock_settime +for ac_func in clock_getres clock_gettime clock_settime lstat readlink do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac index c8629a8..3d1fbc3 100644 --- a/libsanitizer/configure.ac +++ b/libsanitizer/configure.ac @@ -95,7 +95,7 @@ AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"]) AM_CONDITIONAL(ESAN_SUPPORTED, [test "x$ESAN_SUPPORTED" = "xyes"]) # Check for functions needed. -AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime) +AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink) # Common libraries that we need to link against for all sanitizer libs. link_sanitizer_common='-lpthread -lm' -- 2.7.4 From 0502d35d916d65279b6afa7399018077efde2659 Mon Sep 17 00:00:00 2001 From: Denis Khalikov Date: Tue, 19 Jun 2018 13:12:36 +0300 Subject: [PATCH 08/16] PR other/86198 * elf.c (elf_add): Increase ".note.gnu.build-id" section size checking up to 36 bytes. Change-Id: I5a6046a551395ad61c8013a347c875ad22b33599 --- libbacktrace/ChangeLog | 6 ++++++ libbacktrace/elf.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog index ad0ab3c..f9497d1 100644 --- a/libbacktrace/ChangeLog +++ b/libbacktrace/ChangeLog @@ -1,3 +1,9 @@ +2018-06-18 Denis Khalikov + + PR other/86198 + * elf.c (elf_add): Increase ".note.gnu.build-id" section size + checking up to 36 bytes. + 2018-01-16 Ian Lance Taylor * elf.c (codes) [GENERATE_FIXED_HUFFMAN_TABLE]: Fix size to be diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c index f923bc2..f44bd08 100644 --- a/libbacktrace/elf.c +++ b/libbacktrace/elf.c @@ -2820,7 +2820,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (note->type == NT_GNU_BUILD_ID && note->namesz == 4 && strncmp (note->name, "GNU", 4) == 0 - && shdr->sh_size < 12 + ((note->namesz + 3) & ~ 3) + note->descsz) + && shdr->sh_size <= 12 + ((note->namesz + 3) & ~ 3) + note->descsz) { buildid_data = ¬e->name[0] + ((note->namesz + 3) & ~ 3); buildid_size = note->descsz; -- 2.7.4 From ea6505b08f4afe73eb99445c4a26eccbfcd7db70 Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Fri, 22 Jun 2018 15:21:18 +0300 Subject: [PATCH 09/16] Return memory to OS right after free (not in the async thread). Summary: In order to avoid starting a separate thread to return unused memory to the system (the thread interferes with process startup on Android, Zygota waits for all threads to exit before fork, but this thread never exits), try to return it right after free. Reviewers: eugenis Subscribers: cryptoad, filcab, danalbert, kubabrecka, llvm-commits Patch by Aleksey Shlyapnikov. Differential Revision: https://reviews.llvm.org/D27003 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@288091 91177308-0d34-0410-b5e6-96231b3b80d8 Change-Id: I2e01818bbdf3a0b9f0855b28d963b8016000abc8 --- libsanitizer/asan/asan_activation.cc | 6 +- libsanitizer/asan/asan_activation_flags.inc | 1 + libsanitizer/asan/asan_allocator.cc | 11 ++- libsanitizer/asan/asan_allocator.h | 1 + libsanitizer/lsan/lsan_allocator.cc | 4 +- .../sanitizer_common/sanitizer_allocator.cc | 3 +- .../sanitizer_allocator_combined.h | 23 +++--- .../sanitizer_allocator_primary32.h | 14 ++-- .../sanitizer_allocator_primary64.h | 81 +++++++++++++--------- libsanitizer/sanitizer_common/sanitizer_atomic.h | 5 ++ libsanitizer/sanitizer_common/sanitizer_common.h | 10 ++- .../sanitizer_common/sanitizer_common_libcdep.cc | 9 --- libsanitizer/sanitizer_common/sanitizer_flags.inc | 8 ++- libsanitizer/tsan/tsan_mman.cc | 4 +- 14 files changed, 107 insertions(+), 73 deletions(-) diff --git a/libsanitizer/asan/asan_activation.cc b/libsanitizer/asan/asan_activation.cc index ecd767c..26798e7 100644 --- a/libsanitizer/asan/asan_activation.cc +++ b/libsanitizer/asan/asan_activation.cc @@ -77,11 +77,13 @@ static struct AsanDeactivatedFlags { Report( "quarantine_size_mb %d, max_redzone %d, poison_heap %d, " "malloc_context_size %d, alloc_dealloc_mismatch %d, " - "allocator_may_return_null %d, coverage %d, coverage_dir %s\n", + "allocator_may_return_null %d, coverage %d, coverage_dir %s, " + "allocator_release_to_os_interval_ms %d\n", allocator_options.quarantine_size_mb, allocator_options.max_redzone, poison_heap, malloc_context_size, allocator_options.alloc_dealloc_mismatch, - allocator_options.may_return_null, coverage, coverage_dir); + allocator_options.may_return_null, coverage, coverage_dir, + allocator_options.release_to_os_interval_ms); } } asan_deactivated_flags; diff --git a/libsanitizer/asan/asan_activation_flags.inc b/libsanitizer/asan/asan_activation_flags.inc index 4bab382..6805e73 100644 --- a/libsanitizer/asan/asan_activation_flags.inc +++ b/libsanitizer/asan/asan_activation_flags.inc @@ -31,3 +31,4 @@ COMMON_ACTIVATION_FLAG(bool, coverage) COMMON_ACTIVATION_FLAG(const char *, coverage_dir) COMMON_ACTIVATION_FLAG(int, verbosity) COMMON_ACTIVATION_FLAG(bool, help) +COMMON_ACTIVATION_FLAG(s32, allocator_release_to_os_interval_ms) diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc index d3ddb90..916d284 100644 --- a/libsanitizer/asan/asan_allocator.cc +++ b/libsanitizer/asan/asan_allocator.cc @@ -209,6 +209,7 @@ void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) { max_redzone = f->max_redzone; may_return_null = cf->allocator_may_return_null; alloc_dealloc_mismatch = f->alloc_dealloc_mismatch; + release_to_os_interval_ms = cf->allocator_release_to_os_interval_ms; } void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) { @@ -217,6 +218,7 @@ void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) { f->max_redzone = max_redzone; cf->allocator_may_return_null = may_return_null; f->alloc_dealloc_mismatch = alloc_dealloc_mismatch; + cf->allocator_release_to_os_interval_ms = release_to_os_interval_ms; } struct Allocator { @@ -260,7 +262,7 @@ struct Allocator { } void Initialize(const AllocatorOptions &options) { - allocator.Init(options.may_return_null); + allocator.Init(options.may_return_null, options.release_to_os_interval_ms); SharedInitCode(options); } @@ -289,6 +291,7 @@ struct Allocator { void ReInitialize(const AllocatorOptions &options) { allocator.SetMayReturnNull(options.may_return_null); + allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms); SharedInitCode(options); // Poison all existing allocation's redzones. @@ -310,6 +313,7 @@ struct Allocator { options->may_return_null = allocator.MayReturnNull(); options->alloc_dealloc_mismatch = atomic_load(&alloc_dealloc_mismatch, memory_order_acquire); + options->release_to_os_interval_ms = allocator.ReleaseToOSIntervalMs(); } // -------------------- Helper methods. ------------------------- @@ -685,8 +689,6 @@ struct Allocator { fallback_mutex.Unlock(); allocator.ForceUnlock(); } - - void ReleaseToOS() { allocator.ReleaseToOS(); } }; static Allocator instance(LINKER_INITIALIZED); @@ -728,11 +730,8 @@ StackTrace AsanChunkView::GetFreeStack() { return GetStackTraceFromId(GetFreeStackId()); } -void ReleaseToOS() { instance.ReleaseToOS(); } - void InitializeAllocator(const AllocatorOptions &options) { instance.Initialize(options); - SetAllocatorReleaseToOSCallback(ReleaseToOS); } void ReInitializeAllocator(const AllocatorOptions &options) { diff --git a/libsanitizer/asan/asan_allocator.h b/libsanitizer/asan/asan_allocator.h index 7aa1a92..70a3aa9 100644 --- a/libsanitizer/asan/asan_allocator.h +++ b/libsanitizer/asan/asan_allocator.h @@ -35,6 +35,7 @@ struct AllocatorOptions { u16 max_redzone; u8 may_return_null; u8 alloc_dealloc_mismatch; + s32 release_to_os_interval_ms; void SetFrom(const Flags *f, const CommonFlags *cf); void CopyTo(Flags *f, CommonFlags *cf); diff --git a/libsanitizer/lsan/lsan_allocator.cc b/libsanitizer/lsan/lsan_allocator.cc index bc18d56..4cd4509 100644 --- a/libsanitizer/lsan/lsan_allocator.cc +++ b/libsanitizer/lsan/lsan_allocator.cc @@ -79,7 +79,9 @@ static Allocator allocator; static THREADLOCAL AllocatorCache cache; void InitializeAllocator() { - allocator.InitLinkerInitialized(common_flags()->allocator_may_return_null); + allocator.InitLinkerInitialized( + common_flags()->allocator_may_return_null, + common_flags()->allocator_release_to_os_interval_ms); } void AllocatorThreadFinish() { diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.cc b/libsanitizer/sanitizer_common/sanitizer_allocator.cc index 2755853..e2365bf 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.cc +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.cc @@ -92,7 +92,8 @@ InternalAllocator *internal_allocator() { SpinMutexLock l(&internal_alloc_init_mu); if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) == 0) { - internal_allocator_instance->Init(/* may_return_null*/ false); + internal_allocator_instance->Init( + /* may_return_null */ false, kReleaseToOSIntervalNever); atomic_store(&internal_allocator_initialized, 1, memory_order_release); } } diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h index 2fdd85b..5992705 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h @@ -22,21 +22,22 @@ template // NOLINT class CombinedAllocator { public: - void InitCommon(bool may_return_null) { - primary_.Init(); + void InitCommon(bool may_return_null, s32 release_to_os_interval_ms) { + primary_.Init(release_to_os_interval_ms); atomic_store(&may_return_null_, may_return_null, memory_order_relaxed); } - void InitLinkerInitialized(bool may_return_null) { + void InitLinkerInitialized( + bool may_return_null, s32 release_to_os_interval_ms) { secondary_.InitLinkerInitialized(may_return_null); stats_.InitLinkerInitialized(); - InitCommon(may_return_null); + InitCommon(may_return_null, release_to_os_interval_ms); } - void Init(bool may_return_null) { + void Init(bool may_return_null, s32 release_to_os_interval_ms) { secondary_.Init(may_return_null); stats_.Init(); - InitCommon(may_return_null); + InitCommon(may_return_null, release_to_os_interval_ms); } void *Allocate(AllocatorCache *cache, uptr size, uptr alignment, @@ -81,6 +82,14 @@ class CombinedAllocator { atomic_store(&may_return_null_, may_return_null, memory_order_release); } + s32 ReleaseToOSIntervalMs() const { + return primary_.ReleaseToOSIntervalMs(); + } + + void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) { + primary_.SetReleaseToOSIntervalMs(release_to_os_interval_ms); + } + bool RssLimitIsExceeded() { return atomic_load(&rss_limit_is_exceeded_, memory_order_acquire); } @@ -197,8 +206,6 @@ class CombinedAllocator { return true; } - void ReleaseToOS() { primary_.ReleaseToOS(); } - // Iterate over all existing chunks. // The allocator must be locked when calling this function. void ForEachChunk(ForEachChunkCallback callback, void *arg) { diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h b/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h index dddd240..ca5926d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h @@ -89,11 +89,19 @@ class SizeClassAllocator32 { SizeClassMap, kRegionSizeLog, ByteMap, MapUnmapCallback> ThisT; typedef SizeClassAllocator32LocalCache AllocatorCache; - void Init() { + void Init(s32 release_to_os_interval_ms) { possible_regions.TestOnlyInit(); internal_memset(size_class_info_array, 0, sizeof(size_class_info_array)); } + s32 ReleaseToOSIntervalMs() const { + return kReleaseToOSIntervalNever; + } + + void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) { + // This is empty here. Currently only implemented in 64-bit allocator. + } + void *MapWithCallback(uptr size) { size = RoundUpTo(size, GetPageSizeCached()); void *res = MmapOrDie(size, "SizeClassAllocator32"); @@ -239,10 +247,6 @@ class SizeClassAllocator32 { return 0; } - // This is empty here. Currently only implemented in 64-bit allocator. - void ReleaseToOS() { } - - typedef SizeClassMap SizeClassMapT; static const uptr kNumClasses = SizeClassMap::kNumClasses; diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h b/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h index 5afc856..19c6606 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h @@ -67,7 +67,7 @@ class SizeClassAllocator64 { return base + (static_cast(ptr32) << kCompactPtrScale); } - void Init() { + void Init(s32 release_to_os_interval_ms) { uptr TotalSpaceSize = kSpaceSize + AdditionalSize(); if (kUsingConstantSpaceBeg) { CHECK_EQ(kSpaceBeg, reinterpret_cast( @@ -77,9 +77,19 @@ class SizeClassAllocator64 { reinterpret_cast(MmapNoAccess(TotalSpaceSize)); CHECK_NE(NonConstSpaceBeg, ~(uptr)0); } + SetReleaseToOSIntervalMs(release_to_os_interval_ms); MapWithCallbackOrDie(SpaceEnd(), AdditionalSize()); } + s32 ReleaseToOSIntervalMs() const { + return atomic_load(&release_to_os_interval_ms_, memory_order_relaxed); + } + + void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) { + atomic_store(&release_to_os_interval_ms_, release_to_os_interval_ms, + memory_order_relaxed); + } + static bool CanAllocate(uptr size, uptr alignment) { return size <= SizeClassMap::kMaxSize && alignment <= SizeClassMap::kMaxSize; @@ -291,11 +301,6 @@ class SizeClassAllocator64 { GetPageSizeCached()); } - void ReleaseToOS() { - for (uptr class_id = 1; class_id < kNumClasses; class_id++) - ReleaseToOS(class_id); - } - typedef SizeClassMap SizeClassMapT; static const uptr kNumClasses = SizeClassMap::kNumClasses; static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded; @@ -324,8 +329,8 @@ class SizeClassAllocator64 { static const uptr kMetaMapSize = 1 << 16; // Call mmap for free array memory with at least this size. static const uptr kFreeArrayMapSize = 1 << 16; - // Granularity of ReleaseToOs (aka madvise). - static const uptr kReleaseToOsGranularity = 1 << 12; + + atomic_sint32_t release_to_os_interval_ms_; struct Stats { uptr n_allocated; @@ -335,6 +340,7 @@ class SizeClassAllocator64 { struct ReleaseToOsInfo { uptr n_freed_at_last_release; uptr num_releases; + u64 last_release_at_ns; }; struct RegionInfo { @@ -507,50 +513,63 @@ class SizeClassAllocator64 { CompactPtrT first, CompactPtrT last) { uptr beg_ptr = CompactPtrToPointer(region_beg, first); uptr end_ptr = CompactPtrToPointer(region_beg, last) + chunk_size; - CHECK_GE(end_ptr - beg_ptr, kReleaseToOsGranularity); - beg_ptr = RoundUpTo(beg_ptr, kReleaseToOsGranularity); - end_ptr = RoundDownTo(end_ptr, kReleaseToOsGranularity); + const uptr page_size = GetPageSizeCached(); + CHECK_GE(end_ptr - beg_ptr, page_size); + beg_ptr = RoundUpTo(beg_ptr, page_size); + end_ptr = RoundDownTo(end_ptr, page_size); if (end_ptr == beg_ptr) return false; ReleaseMemoryToOS(beg_ptr, end_ptr - beg_ptr); return true; } - // Releases some RAM back to OS. + // Attempts to release some RAM back to OS. The region is expected to be + // locked. // Algorithm: - // * Lock the region. // * Sort the chunks. // * Find ranges fully covered by free-d chunks // * Release them to OS with madvise. - // - // TODO(kcc): make sure we don't do it too frequently. - void ReleaseToOS(uptr class_id) { + void MaybeReleaseToOS(uptr class_id) { RegionInfo *region = GetRegionInfo(class_id); + const uptr chunk_size = ClassIdToSize(class_id); + const uptr page_size = GetPageSizeCached(); + + uptr n = region->num_freed_chunks; + if (n * chunk_size < page_size) + return; // No chance to release anything. + if ((region->n_freed - region->rtoi.n_freed_at_last_release) * chunk_size < + page_size) { + return; // Nothing new to release. + } + + s32 interval_ms = ReleaseToOSIntervalMs(); + if (interval_ms < 0) + return; + + u64 now_ns = NanoTime(); + if (region->rtoi.last_release_at_ns + interval_ms * 1000000ULL > now_ns) + return; // Memory was returned recently. + region->rtoi.last_release_at_ns = now_ns; + uptr region_beg = GetRegionBeginBySizeClass(class_id); CompactPtrT *free_array = GetFreeArray(region_beg); - uptr chunk_size = ClassIdToSize(class_id); - uptr scaled_chunk_size = chunk_size >> kCompactPtrScale; - const uptr kScaledGranularity = kReleaseToOsGranularity >> kCompactPtrScale; - BlockingMutexLock l(®ion->mutex); - uptr n = region->num_freed_chunks; - if (n * chunk_size < kReleaseToOsGranularity) - return; // No chance to release anything. - if ((region->rtoi.n_freed_at_last_release - region->stats.n_freed) * chunk_size < - kReleaseToOsGranularity) - return; // Nothing new to release. SortArray(free_array, n); - uptr beg = free_array[0]; + + const uptr scaled_chunk_size = chunk_size >> kCompactPtrScale; + const uptr kScaledGranularity = page_size >> kCompactPtrScale; + + uptr range_beg = free_array[0]; uptr prev = free_array[0]; for (uptr i = 1; i < n; i++) { uptr chunk = free_array[i]; CHECK_GT(chunk, prev); if (chunk - prev != scaled_chunk_size) { CHECK_GT(chunk - prev, scaled_chunk_size); - if (prev + scaled_chunk_size - beg >= kScaledGranularity) { - MaybeReleaseChunkRange(region_beg, chunk_size, beg, prev); - region->rtoi.n_freed_at_last_release = region->stats.n_freed; + if (prev + scaled_chunk_size - range_beg >= kScaledGranularity) { + MaybeReleaseChunkRange(region_beg, chunk_size, range_beg, prev); + region->rtoi.n_freed_at_last_release = region->n_freed; region->rtoi.num_releases++; } - beg = chunk; + range_beg = chunk; } prev = chunk; } diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic.h b/libsanitizer/sanitizer_common/sanitizer_atomic.h index 4973b7d..82de0c6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_atomic.h +++ b/libsanitizer/sanitizer_common/sanitizer_atomic.h @@ -35,6 +35,11 @@ struct atomic_uint16_t { volatile Type val_dont_use; }; +struct atomic_sint32_t { + typedef s32 Type; + volatile Type val_dont_use; +}; + struct atomic_uint32_t { typedef u32 Type; volatile Type val_dont_use; diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h index 592c5fc..140ff3e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.h +++ b/libsanitizer/sanitizer_common/sanitizer_common.h @@ -381,12 +381,6 @@ void SetCheckFailedCallback(CheckFailedCallbackType callback); // The callback should be registered once at the tool init time. void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)); -// Callback to be called when we want to try releasing unused allocator memory -// back to the OS. -typedef void (*AllocatorReleaseToOSCallback)(); -// The callback should be registered once at the tool init time. -void SetAllocatorReleaseToOSCallback(AllocatorReleaseToOSCallback Callback); - // Functions related to signal handling. typedef void (*SignalHandlerType)(int, void *, void *); bool IsHandledDeadlySignal(int signum); @@ -851,6 +845,10 @@ struct StackDepotStats { uptr allocated; }; +// The default value for allocator_release_to_os_interval_ms common flag to +// indicate that sanitizer allocator should not attempt to release memory to OS. +const s32 kReleaseToOSIntervalNever = -1; + } // namespace __sanitizer inline void *operator new(__sanitizer::operator_new_size_type size, diff --git a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc index 8c9fa98..a50ab14 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc @@ -68,18 +68,11 @@ void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) { SoftRssLimitExceededCallback = Callback; } -static AllocatorReleaseToOSCallback ReleseCallback; -void SetAllocatorReleaseToOSCallback(AllocatorReleaseToOSCallback Callback) { - CHECK_EQ(ReleseCallback, nullptr); - ReleseCallback = Callback; -} - #if SANITIZER_LINUX && !SANITIZER_GO void BackgroundThread(void *arg) { uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb; uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb; bool heap_profile = common_flags()->heap_profile; - bool allocator_release_to_os = common_flags()->allocator_release_to_os; uptr prev_reported_rss = 0; uptr prev_reported_stack_depot_size = 0; bool reached_soft_rss_limit = false; @@ -125,7 +118,6 @@ void BackgroundThread(void *arg) { SoftRssLimitExceededCallback(false); } } - if (allocator_release_to_os && ReleseCallback) ReleseCallback(); if (heap_profile && current_rss_mb > rss_during_last_reported_profile * 1.1) { Printf("\n\nHEAP PROFILE at RSS %zdMb\n", current_rss_mb); @@ -160,7 +152,6 @@ void MaybeStartBackgroudThread() { // Start the background thread if one of the rss limits is given. if (!common_flags()->hard_rss_limit_mb && !common_flags()->soft_rss_limit_mb && - !common_flags()->allocator_release_to_os && !common_flags()->heap_profile) return; if (!&real_pthread_create) return; // Can't spawn the thread anyway. internal_start_thread(BackgroundThread, nullptr); diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.inc b/libsanitizer/sanitizer_common/sanitizer_flags.inc index 36e83f9..cb49473 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.inc +++ b/libsanitizer/sanitizer_common/sanitizer_flags.inc @@ -118,9 +118,11 @@ COMMON_FLAG(uptr, soft_rss_limit_mb, 0, " This limit does not affect memory allocations other than" " malloc/new.") COMMON_FLAG(bool, heap_profile, false, "Experimental heap profiler, asan-only") -COMMON_FLAG(bool, allocator_release_to_os, false, - "Experimental. If true, try to periodically release unused" - " memory to the OS.\n") +COMMON_FLAG(s32, allocator_release_to_os_interval_ms, kReleaseToOSIntervalNever, + "Experimental. Only affects a 64-bit allocator. If set, tries to " + "release unused memory to the OS, but not more often than this " + "interval (in milliseconds). Negative values mean do not attempt " + "to release memory to the OS.\n") COMMON_FLAG(bool, can_use_proc_maps_statm, true, "If false, do not attempt to read /proc/maps/statm." " Mostly useful for testing sanitizers.") diff --git a/libsanitizer/tsan/tsan_mman.cc b/libsanitizer/tsan/tsan_mman.cc index 152c2de..ebb79c6 100644 --- a/libsanitizer/tsan/tsan_mman.cc +++ b/libsanitizer/tsan/tsan_mman.cc @@ -109,7 +109,9 @@ ScopedGlobalProcessor::~ScopedGlobalProcessor() { } void InitializeAllocator() { - allocator()->Init(common_flags()->allocator_may_return_null); + allocator()->Init( + common_flags()->allocator_may_return_null, + common_flags()->allocator_release_to_os_interval_ms); } void InitializeAllocatorLate() { -- 2.7.4 From 53f3195f0638977d702afd4003b1985509c80263 Mon Sep 17 00:00:00 2001 From: Kostya Kortchinsky Date: Fri, 22 Jun 2018 15:26:11 +0300 Subject: [PATCH 10/16] Corrected D27428: Do not use the alignment-rounded-up size with secondary Summary: I atually had an integer overflow on 32-bit with D27428 that didn't reproduce locally, as the test servers would manage allocate addresses in the 0xffffxxxx range, which led to some issues when rounding addresses. At this point, I feel that Scudo could benefit from having its own combined allocator, as we don't get any benefit from the current one, but have to work around some hurdles (alignment checks, rounding up that is no longer needed, extraneous code). Reviewers: kcc, alekseyshl Subscribers: llvm-commits, kubabrecka Differential Revision: https://reviews.llvm.org/D27681 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@289572 91177308-0d34-0410-b5e6-96231b3b80d8 Change-Id: If9f3b384566cffa0bdd1740ef2266a6a9d5f6fa4 --- .../sanitizer_common/sanitizer_allocator_combined.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h index 5992705..159e755 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h @@ -47,16 +47,30 @@ class CombinedAllocator { size = 1; if (size + alignment < size) return ReturnNullOrDieOnBadRequest(); if (check_rss_limit && RssLimitIsExceeded()) return ReturnNullOrDieOnOOM(); + uptr original_size = size; + // If alignment requirements are to be fulfilled by the frontend allocator + // rather than by the primary or secondary, passing an alignment lower than + // or equal to 8 will prevent any further rounding up, as well as the later + // alignment check. if (alignment > 8) size = RoundUpTo(size, alignment); void *res; bool from_primary = primary_.CanAllocate(size, alignment); + // The primary allocator should return a 2^x aligned allocation when + // requested 2^x bytes, hence using the rounded up 'size' when being + // serviced by the primary (this is no longer true when the primary is + // using a non-fixed base address). The secondary takes care of the + // alignment without such requirement, and allocating 'size' would use + // extraneous memory, so we employ 'original_size'. if (from_primary) res = cache->Allocate(&primary_, primary_.ClassID(size)); else - res = secondary_.Allocate(&stats_, size, alignment); + res = secondary_.Allocate(&stats_, original_size, alignment); if (alignment > 8) CHECK_EQ(reinterpret_cast(res) & (alignment - 1), 0); + // When serviced by the secondary, the chunk comes from a mmap allocation + // and will be zero'd out anyway. We only need to clear our the chunk if + // it was serviced by the primary, hence using the rounded up 'size'. if (cleared && res && from_primary) internal_bzero_aligned16(res, RoundUpTo(size, 16)); return res; -- 2.7.4 From 61cc482444c40caf6cf331fd73fc04850566ef31 Mon Sep 17 00:00:00 2001 From: Alex Shlyapnikov Date: Fri, 22 Jun 2018 15:27:29 +0300 Subject: [PATCH 11/16] [ASan] Move rss_limit_is_exceeded_ flag to ASan. Summary: Move the OOM decision based on RSS limits out of generic allocator to ASan allocator, where it makes more sense at the moment. Reviewers: eugenis Subscribers: kubamracek, llvm-commits Differential Revision: https://reviews.llvm.org/D34180 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@305342 91177308-0d34-0410-b5e6-96231b3b80d8 Change-Id: I2c7ccdfbfd420b74cbc5c96a50933bace9b88ccf --- libsanitizer/asan/asan_allocator.cc | 21 ++++++++++++++++----- .../sanitizer_common/sanitizer_allocator_combined.h | 19 +++++-------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc index 916d284..b6e2cfa 100644 --- a/libsanitizer/asan/asan_allocator.cc +++ b/libsanitizer/asan/asan_allocator.cc @@ -233,6 +233,8 @@ struct Allocator { AllocatorCache fallback_allocator_cache; QuarantineCache fallback_quarantine_cache; + atomic_uint8_t rss_limit_exceeded; + // ------------------- Options -------------------------- atomic_uint16_t min_redzone; atomic_uint16_t max_redzone; @@ -266,6 +268,14 @@ struct Allocator { SharedInitCode(options); } + bool RssLimitExceeded() { + return atomic_load(&rss_limit_exceeded, memory_order_relaxed); + } + + void SetRssLimitExceeded(bool limit_exceeded) { + atomic_store(&rss_limit_exceeded, limit_exceeded, memory_order_relaxed); + } + void RePoisonChunk(uptr chunk) { // This could a user-facing chunk (with redzones), or some internal // housekeeping chunk, like TransferBatch. Start by assuming the former. @@ -360,6 +370,8 @@ struct Allocator { AllocType alloc_type, bool can_fill) { if (UNLIKELY(!asan_inited)) AsanInitFromRtl(); + if (RssLimitExceeded()) + return allocator.ReturnNullOrDieOnOOM(); Flags &fl = *flags(); CHECK(stack); const uptr min_alignment = SHADOW_GRANULARITY; @@ -397,16 +409,15 @@ struct Allocator { AsanThread *t = GetCurrentThread(); void *allocated; - bool check_rss_limit = true; if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); allocated = - allocator.Allocate(cache, needed_size, 8, false, check_rss_limit); + allocator.Allocate(cache, needed_size, 8, false); } else { SpinMutexLock l(&fallback_mutex); AllocatorCache *cache = &fallback_allocator_cache; allocated = - allocator.Allocate(cache, needed_size, 8, false, check_rss_limit); + allocator.Allocate(cache, needed_size, 8, false); } if (!allocated) return allocator.ReturnNullOrDieOnOOM(); @@ -833,8 +844,8 @@ void asan_mz_force_unlock() { instance.ForceUnlock(); } -void AsanSoftRssLimitExceededCallback(bool exceeded) { - instance.allocator.SetRssLimitIsExceeded(exceeded); +void AsanSoftRssLimitExceededCallback(bool limit_exceeded) { + instance.SetRssLimitExceeded(limit_exceeded); } } // namespace __asan diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h index 159e755..3f64324 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h @@ -41,12 +41,12 @@ class CombinedAllocator { } void *Allocate(AllocatorCache *cache, uptr size, uptr alignment, - bool cleared = false, bool check_rss_limit = false) { + bool cleared = false) { // Returning 0 on malloc(0) may break a lot of code. if (size == 0) size = 1; - if (size + alignment < size) return ReturnNullOrDieOnBadRequest(); - if (check_rss_limit && RssLimitIsExceeded()) return ReturnNullOrDieOnOOM(); + if (size + alignment < size) + return ReturnNullOrDieOnBadRequest(); uptr original_size = size; // If alignment requirements are to be fulfilled by the frontend allocator // rather than by the primary or secondary, passing an alignment lower than @@ -87,7 +87,8 @@ class CombinedAllocator { } void *ReturnNullOrDieOnOOM() { - if (MayReturnNull()) return nullptr; + if (MayReturnNull()) + return nullptr; ReportAllocatorCannotReturnNull(true); } @@ -104,15 +105,6 @@ class CombinedAllocator { primary_.SetReleaseToOSIntervalMs(release_to_os_interval_ms); } - bool RssLimitIsExceeded() { - return atomic_load(&rss_limit_is_exceeded_, memory_order_acquire); - } - - void SetRssLimitIsExceeded(bool rss_limit_is_exceeded) { - atomic_store(&rss_limit_is_exceeded_, rss_limit_is_exceeded, - memory_order_release); - } - void Deallocate(AllocatorCache *cache, void *p) { if (!p) return; if (primary_.PointerIsMine(p)) @@ -232,5 +224,4 @@ class CombinedAllocator { SecondaryAllocator secondary_; AllocatorGlobalStats stats_; atomic_uint8_t may_return_null_; - atomic_uint8_t rss_limit_is_exceeded_; }; -- 2.7.4 From b7147344514fc69ed46c29e362ac3782cc1b2315 Mon Sep 17 00:00:00 2001 From: Alex Shlyapnikov Date: Fri, 22 Jun 2018 15:30:32 +0300 Subject: [PATCH 12/16] [Sanitizer] Remove CombinedAllocator::Allocate's 'cleared' parameter Summary: CombinedAllocator::Allocate cleared parameter is not used anywhere and seem to be obsolete. Reviewers: eugenis Subscribers: llvm-commits, kubamracek Differential Revision: https://reviews.llvm.org/D34289 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@305590 91177308-0d34-0410-b5e6-96231b3b80d8 Change-Id: I57958f6ea291bb31027070f93ad652598c69d1fe --- libsanitizer/asan/asan_allocator.cc | 8 +++----- libsanitizer/lsan/lsan_allocator.cc | 2 +- libsanitizer/sanitizer_common/sanitizer_allocator.cc | 4 ++-- libsanitizer/sanitizer_common/sanitizer_allocator_combined.h | 8 +------- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc index b6e2cfa..2dac064 100644 --- a/libsanitizer/asan/asan_allocator.cc +++ b/libsanitizer/asan/asan_allocator.cc @@ -158,7 +158,7 @@ struct QuarantineCallback { } void *Allocate(uptr size) { - return get_allocator().Allocate(cache_, size, 1, false); + return get_allocator().Allocate(cache_, size, 1); } void Deallocate(void *p) { @@ -411,13 +411,11 @@ struct Allocator { void *allocated; if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); - allocated = - allocator.Allocate(cache, needed_size, 8, false); + allocated = allocator.Allocate(cache, needed_size, 8); } else { SpinMutexLock l(&fallback_mutex); AllocatorCache *cache = &fallback_allocator_cache; - allocated = - allocator.Allocate(cache, needed_size, 8, false); + allocated = allocator.Allocate(cache, needed_size, 8); } if (!allocated) return allocator.ReturnNullOrDieOnOOM(); diff --git a/libsanitizer/lsan/lsan_allocator.cc b/libsanitizer/lsan/lsan_allocator.cc index 4cd4509..ef0ca19 100644 --- a/libsanitizer/lsan/lsan_allocator.cc +++ b/libsanitizer/lsan/lsan_allocator.cc @@ -117,7 +117,7 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment, Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size); return nullptr; } - void *p = allocator.Allocate(&cache, size, alignment, false); + void *p = allocator.Allocate(&cache, size, alignment); // Do not rely on the allocator to clear the memory (it's slow). if (cleared && allocator.FromPrimary(p)) memset(p, 0, size); diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.cc b/libsanitizer/sanitizer_common/sanitizer_allocator.cc index e2365bf..156792c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.cc +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.cc @@ -106,9 +106,9 @@ static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache, if (cache == 0) { SpinMutexLock l(&internal_allocator_cache_mu); return internal_allocator()->Allocate(&internal_allocator_cache, size, - alignment, false); + alignment); } - return internal_allocator()->Allocate(cache, size, alignment, false); + return internal_allocator()->Allocate(cache, size, alignment); } static void *RawInternalRealloc(void *ptr, uptr size, diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h index 3f64324..216a6fd 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h @@ -40,8 +40,7 @@ class CombinedAllocator { InitCommon(may_return_null, release_to_os_interval_ms); } - void *Allocate(AllocatorCache *cache, uptr size, uptr alignment, - bool cleared = false) { + void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) { // Returning 0 on malloc(0) may break a lot of code. if (size == 0) size = 1; @@ -68,11 +67,6 @@ class CombinedAllocator { res = secondary_.Allocate(&stats_, original_size, alignment); if (alignment > 8) CHECK_EQ(reinterpret_cast(res) & (alignment - 1), 0); - // When serviced by the secondary, the chunk comes from a mmap allocation - // and will be zero'd out anyway. We only need to clear our the chunk if - // it was serviced by the primary, hence using the rounded up 'size'. - if (cleared && res && from_primary) - internal_bzero_aligned16(res, RoundUpTo(size, 16)); return res; } -- 2.7.4 From aa38e508ce4649c02dba65e132b56a6d98198bf5 Mon Sep 17 00:00:00 2001 From: Alex Shlyapnikov Date: Fri, 22 Jun 2018 15:33:11 +0300 Subject: [PATCH 13/16] [Sanitizers] Move cached allocator_may_return_null flag to sanitizer_allocator Summary: Move cached allocator_may_return_null flag to sanitizer_allocator.cc and provide API to consolidate and unify the behavior of all specific allocators. Make all sanitizers using CombinedAllocator to follow AllocatorReturnNullOrDieOnOOM() rules to behave the same way when OOM happens. When OOM happens, turn allocator_out_of_memory flag on regardless of allocator_may_return_null flag value (it used to not to be set when allocator_may_return_null == true). release_to_os_interval_ms and rss_limit_exceeded will likely be moved to sanitizer_allocator.cc too (later). Reviewers: eugenis Subscribers: srhines, kubamracek, llvm-commits Differential Revision: https://reviews.llvm.org/D34310 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@305858 91177308-0d34-0410-b5e6-96231b3b80d8 Change-Id: I29ae28a1f9c569d69666936111e307c3aecf9314 --- libsanitizer/asan/asan_allocator.cc | 17 ++++---- libsanitizer/lsan/lsan_allocator.cc | 2 +- .../sanitizer_common/sanitizer_allocator.cc | 47 +++++++++++++++++---- .../sanitizer_common/sanitizer_allocator.h | 26 +++++++++--- .../sanitizer_allocator_combined.h | 49 ++++++---------------- .../sanitizer_allocator_internal.h | 3 +- .../sanitizer_allocator_secondary.h | 35 ++++------------ .../sanitizer_symbolizer_posix_libcdep.cc | 2 +- libsanitizer/tsan/tsan_mman.cc | 9 ++-- 9 files changed, 99 insertions(+), 91 deletions(-) diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc index 2dac064..1f15235 100644 --- a/libsanitizer/asan/asan_allocator.cc +++ b/libsanitizer/asan/asan_allocator.cc @@ -264,7 +264,8 @@ struct Allocator { } void Initialize(const AllocatorOptions &options) { - allocator.Init(options.may_return_null, options.release_to_os_interval_ms); + SetAllocatorMayReturnNull(options.may_return_null); + allocator.Init(options.release_to_os_interval_ms); SharedInitCode(options); } @@ -300,7 +301,7 @@ struct Allocator { } void ReInitialize(const AllocatorOptions &options) { - allocator.SetMayReturnNull(options.may_return_null); + SetAllocatorMayReturnNull(options.may_return_null); allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms); SharedInitCode(options); @@ -320,7 +321,7 @@ struct Allocator { options->quarantine_size_mb = quarantine.GetSize() >> 20; options->min_redzone = atomic_load(&min_redzone, memory_order_acquire); options->max_redzone = atomic_load(&max_redzone, memory_order_acquire); - options->may_return_null = allocator.MayReturnNull(); + options->may_return_null = AllocatorMayReturnNull(); options->alloc_dealloc_mismatch = atomic_load(&alloc_dealloc_mismatch, memory_order_acquire); options->release_to_os_interval_ms = allocator.ReleaseToOSIntervalMs(); @@ -371,7 +372,7 @@ struct Allocator { if (UNLIKELY(!asan_inited)) AsanInitFromRtl(); if (RssLimitExceeded()) - return allocator.ReturnNullOrDieOnOOM(); + return AsanAllocator::FailureHandler::OnOOM(); Flags &fl = *flags(); CHECK(stack); const uptr min_alignment = SHADOW_GRANULARITY; @@ -404,7 +405,7 @@ struct Allocator { if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n", (void*)size); - return allocator.ReturnNullOrDieOnBadRequest(); + return AsanAllocator::FailureHandler::OnBadRequest(); } AsanThread *t = GetCurrentThread(); @@ -417,8 +418,8 @@ struct Allocator { AllocatorCache *cache = &fallback_allocator_cache; allocated = allocator.Allocate(cache, needed_size, 8); } - - if (!allocated) return allocator.ReturnNullOrDieOnOOM(); + if (!allocated) + return nullptr; if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) { // Heap poisoning is enabled, but the allocator provides an unpoisoned @@ -607,7 +608,7 @@ struct Allocator { void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { if (CallocShouldReturnNullDueToOverflow(size, nmemb)) - return allocator.ReturnNullOrDieOnBadRequest(); + return AsanAllocator::FailureHandler::OnBadRequest(); void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); // If the memory comes from the secondary allocator no need to clear it // as it comes directly from mmap. diff --git a/libsanitizer/lsan/lsan_allocator.cc b/libsanitizer/lsan/lsan_allocator.cc index ef0ca19..2203506 100644 --- a/libsanitizer/lsan/lsan_allocator.cc +++ b/libsanitizer/lsan/lsan_allocator.cc @@ -79,8 +79,8 @@ static Allocator allocator; static THREADLOCAL AllocatorCache cache; void InitializeAllocator() { + SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null); allocator.InitLinkerInitialized( - common_flags()->allocator_may_return_null, common_flags()->allocator_release_to_os_interval_ms); } diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.cc b/libsanitizer/sanitizer_common/sanitizer_allocator.cc index 156792c..5af4fba 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.cc +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.cc @@ -92,8 +92,7 @@ InternalAllocator *internal_allocator() { SpinMutexLock l(&internal_alloc_init_mu); if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) == 0) { - internal_allocator_instance->Init( - /* may_return_null */ false, kReleaseToOSIntervalNever); + internal_allocator_instance->Init(kReleaseToOSIntervalNever); atomic_store(&internal_allocator_initialized, 1, memory_order_release); } } @@ -160,7 +159,7 @@ void *InternalRealloc(void *addr, uptr size, InternalAllocatorCache *cache) { void *InternalCalloc(uptr count, uptr size, InternalAllocatorCache *cache) { if (CallocShouldReturnNullDueToOverflow(count, size)) - return internal_allocator()->ReturnNullOrDieOnBadRequest(); + return InternalAllocator::FailureHandler::OnBadRequest(); void *p = InternalAlloc(count * size, cache); if (p) internal_memset(p, 0, count * size); return p; @@ -207,12 +206,15 @@ bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) { return (max / size) < n; } -static atomic_uint8_t reporting_out_of_memory = {0}; +static atomic_uint8_t allocator_out_of_memory = {0}; +static atomic_uint8_t allocator_may_return_null = {0}; -bool IsReportingOOM() { return atomic_load_relaxed(&reporting_out_of_memory); } +bool IsAllocatorOutOfMemory() { + return atomic_load_relaxed(&allocator_out_of_memory); +} -void NORETURN ReportAllocatorCannotReturnNull(bool out_of_memory) { - if (out_of_memory) atomic_store_relaxed(&reporting_out_of_memory, 1); +// Prints error message and kills the program. +void NORETURN ReportAllocatorCannotReturnNull() { Report("%s's allocator is terminating the process instead of returning 0\n", SanitizerToolName); Report("If you don't like this behavior set allocator_may_return_null=1\n"); @@ -220,4 +222,35 @@ void NORETURN ReportAllocatorCannotReturnNull(bool out_of_memory) { Die(); } +bool AllocatorMayReturnNull() { + return atomic_load(&allocator_may_return_null, memory_order_relaxed); +} + +void SetAllocatorMayReturnNull(bool may_return_null) { + atomic_store(&allocator_may_return_null, may_return_null, + memory_order_relaxed); +} + +void *ReturnNullOrDieOnFailure::OnBadRequest() { + if (AllocatorMayReturnNull()) + return nullptr; + ReportAllocatorCannotReturnNull(); +} + +void *ReturnNullOrDieOnFailure::OnOOM() { + atomic_store_relaxed(&allocator_out_of_memory, 1); + if (AllocatorMayReturnNull()) + return nullptr; + ReportAllocatorCannotReturnNull(); +} + +void *DieOnFailure::OnBadRequest() { + ReportAllocatorCannotReturnNull(); +} + +void *DieOnFailure::OnOOM() { + atomic_store_relaxed(&allocator_out_of_memory, 1); + ReportAllocatorCannotReturnNull(); +} + } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.h b/libsanitizer/sanitizer_common/sanitizer_allocator.h index ba50acd..c316251 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.h @@ -22,12 +22,28 @@ namespace __sanitizer { -// Returns true if ReportAllocatorCannotReturnNull(true) was called. -// Can be use to avoid memory hungry operations. -bool IsReportingOOM(); +// Since flags are immutable and allocator behavior can be changed at runtime +// (unit tests or ASan on Android are some examples), allocator_may_return_null +// flag value is cached here and can be altered later. +bool AllocatorMayReturnNull(); +void SetAllocatorMayReturnNull(bool may_return_null); -// Prints error message and kills the program. -void NORETURN ReportAllocatorCannotReturnNull(bool out_of_memory); +// Allocator failure handling policies: +// Implements AllocatorMayReturnNull policy, returns null when the flag is set, +// dies otherwise. +struct ReturnNullOrDieOnFailure { + static void *OnBadRequest(); + static void *OnOOM(); +}; +// Always dies on the failure. +struct DieOnFailure { + static void *OnBadRequest(); + static void *OnOOM(); +}; + +// Returns true if allocator detected OOM condition. Can be used to avoid memory +// hungry operations. Set when AllocatorReturnNullOrDieOnOOM() is called. +bool IsAllocatorOutOfMemory(); // Allocators call these callbacks on mmap/munmap. struct NoOpMapUnmapCallback { diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h index 216a6fd..3b408d5 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h @@ -22,22 +22,18 @@ template // NOLINT class CombinedAllocator { public: - void InitCommon(bool may_return_null, s32 release_to_os_interval_ms) { - primary_.Init(release_to_os_interval_ms); - atomic_store(&may_return_null_, may_return_null, memory_order_relaxed); - } + typedef typename SecondaryAllocator::FailureHandler FailureHandler; - void InitLinkerInitialized( - bool may_return_null, s32 release_to_os_interval_ms) { - secondary_.InitLinkerInitialized(may_return_null); + void InitLinkerInitialized(s32 release_to_os_interval_ms) { + primary_.Init(release_to_os_interval_ms); + secondary_.InitLinkerInitialized(); stats_.InitLinkerInitialized(); - InitCommon(may_return_null, release_to_os_interval_ms); } - void Init(bool may_return_null, s32 release_to_os_interval_ms) { - secondary_.Init(may_return_null); + void Init(s32 release_to_os_interval_ms) { + primary_.Init(release_to_os_interval_ms); + secondary_.Init(); stats_.Init(); - InitCommon(may_return_null, release_to_os_interval_ms); } void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) { @@ -45,7 +41,7 @@ class CombinedAllocator { if (size == 0) size = 1; if (size + alignment < size) - return ReturnNullOrDieOnBadRequest(); + return FailureHandler::OnBadRequest(); uptr original_size = size; // If alignment requirements are to be fulfilled by the frontend allocator // rather than by the primary or secondary, passing an alignment lower than @@ -53,44 +49,24 @@ class CombinedAllocator { // alignment check. if (alignment > 8) size = RoundUpTo(size, alignment); - void *res; - bool from_primary = primary_.CanAllocate(size, alignment); // The primary allocator should return a 2^x aligned allocation when // requested 2^x bytes, hence using the rounded up 'size' when being // serviced by the primary (this is no longer true when the primary is // using a non-fixed base address). The secondary takes care of the // alignment without such requirement, and allocating 'size' would use // extraneous memory, so we employ 'original_size'. - if (from_primary) + void *res; + if (primary_.CanAllocate(size, alignment)) res = cache->Allocate(&primary_, primary_.ClassID(size)); else res = secondary_.Allocate(&stats_, original_size, alignment); + if (!res) + return FailureHandler::OnOOM(); if (alignment > 8) CHECK_EQ(reinterpret_cast(res) & (alignment - 1), 0); return res; } - bool MayReturnNull() const { - return atomic_load(&may_return_null_, memory_order_acquire); - } - - void *ReturnNullOrDieOnBadRequest() { - if (MayReturnNull()) - return nullptr; - ReportAllocatorCannotReturnNull(false); - } - - void *ReturnNullOrDieOnOOM() { - if (MayReturnNull()) - return nullptr; - ReportAllocatorCannotReturnNull(true); - } - - void SetMayReturnNull(bool may_return_null) { - secondary_.SetMayReturnNull(may_return_null); - atomic_store(&may_return_null_, may_return_null, memory_order_release); - } - s32 ReleaseToOSIntervalMs() const { return primary_.ReleaseToOSIntervalMs(); } @@ -217,5 +193,4 @@ class CombinedAllocator { PrimaryAllocator primary_; SecondaryAllocator secondary_; AllocatorGlobalStats stats_; - atomic_uint8_t may_return_null_; }; diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h b/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h index 6a8da8f..3d32d6d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h @@ -41,7 +41,8 @@ typedef SizeClassAllocatorLocalCache InternalAllocatorCache; typedef CombinedAllocator > InternalAllocator; + LargeMmapAllocator + > InternalAllocator; void *InternalAlloc(uptr size, InternalAllocatorCache *cache = nullptr, uptr alignment = 0); diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_secondary.h b/libsanitizer/sanitizer_common/sanitizer_allocator_secondary.h index 60a3e03..7432f4b 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_secondary.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_secondary.h @@ -15,17 +15,19 @@ // This class can (de)allocate only large chunks of memory using mmap/unmap. // The main purpose of this allocator is to cover large and rare allocation // sizes not covered by more efficient allocators (e.g. SizeClassAllocator64). -template +template class LargeMmapAllocator { public: - void InitLinkerInitialized(bool may_return_null) { + typedef FailureHandlerT FailureHandler; + + void InitLinkerInitialized() { page_size_ = GetPageSizeCached(); - atomic_store(&may_return_null_, may_return_null, memory_order_relaxed); } - void Init(bool may_return_null) { + void Init() { internal_memset(this, 0, sizeof(*this)); - InitLinkerInitialized(may_return_null); + InitLinkerInitialized(); } void *Allocate(AllocatorStats *stat, uptr size, uptr alignment) { @@ -35,11 +37,11 @@ class LargeMmapAllocator { map_size += alignment; // Overflow. if (map_size < size) - return ReturnNullOrDieOnBadRequest(); + return FailureHandler::OnBadRequest(); uptr map_beg = reinterpret_cast( MmapOrDieOnFatalError(map_size, "LargeMmapAllocator")); if (!map_beg) - return ReturnNullOrDieOnOOM(); + return FailureHandler::OnOOM(); CHECK(IsAligned(map_beg, page_size_)); MapUnmapCallback().OnMap(map_beg, map_size); uptr map_end = map_beg + map_size; @@ -73,24 +75,6 @@ class LargeMmapAllocator { return reinterpret_cast(res); } - bool MayReturnNull() const { - return atomic_load(&may_return_null_, memory_order_acquire); - } - - void *ReturnNullOrDieOnBadRequest() { - if (MayReturnNull()) return nullptr; - ReportAllocatorCannotReturnNull(false); - } - - void *ReturnNullOrDieOnOOM() { - if (MayReturnNull()) return nullptr; - ReportAllocatorCannotReturnNull(true); - } - - void SetMayReturnNull(bool may_return_null) { - atomic_store(&may_return_null_, may_return_null, memory_order_release); - } - void Deallocate(AllocatorStats *stat, void *p) { Header *h = GetHeader(p); { @@ -269,6 +253,5 @@ class LargeMmapAllocator { struct Stats { uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64]; } stats; - atomic_uint8_t may_return_null_; SpinMutex mutex_; }; diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc index 3fcd7d0..51487b4 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -461,7 +461,7 @@ static void ChooseSymbolizerTools(IntrusiveList *list, VReport(2, "Symbolizer is disabled.\n"); return; } - if (IsReportingOOM()) { + if (IsAllocatorOutOfMemory()) { VReport(2, "Cannot use internal symbolizer: out of memory\n"); } else if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) { VReport(2, "Using internal symbolizer.\n"); diff --git a/libsanitizer/tsan/tsan_mman.cc b/libsanitizer/tsan/tsan_mman.cc index ebb79c6..93c18d4 100644 --- a/libsanitizer/tsan/tsan_mman.cc +++ b/libsanitizer/tsan/tsan_mman.cc @@ -109,9 +109,8 @@ ScopedGlobalProcessor::~ScopedGlobalProcessor() { } void InitializeAllocator() { - allocator()->Init( - common_flags()->allocator_may_return_null, - common_flags()->allocator_release_to_os_interval_ms); + SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null); + allocator()->Init(common_flags()->allocator_release_to_os_interval_ms); } void InitializeAllocatorLate() { @@ -148,7 +147,7 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) { void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) { if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) - return allocator()->ReturnNullOrDieOnBadRequest(); + return Allocator::FailureHandler::OnBadRequest(); void *p = allocator()->Allocate(&thr->proc()->alloc_cache, sz, align); if (p == 0) return 0; @@ -161,7 +160,7 @@ void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) { void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) { if (CallocShouldReturnNullDueToOverflow(size, n)) - return allocator()->ReturnNullOrDieOnBadRequest(); + return Allocator::FailureHandler::OnBadRequest(); void *p = user_alloc(thr, pc, n * size); if (p) internal_memset(p, 0, n * size); -- 2.7.4 From 9bfb177e32b6d11c620b1b8195e5c111bfbe3495 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Fri, 22 Jun 2018 15:56:46 +0300 Subject: [PATCH 14/16] [asan] Use linker initialization for the allocator This saves ~2 MB of dirty memory footprint. Can be a big deal on mobile devices especially when running multiple processes with ASan. Differential Revision: https://reviews.llvm.org/D40627 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@320660 91177308-0d34-0410-b5e6-96231b3b80d8 Change-Id: I409b9dc7f73e13248987dd38d96a074ffc2ff044 --- libsanitizer/asan/asan_allocator.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc index 1f15235..7bb766d 100644 --- a/libsanitizer/asan/asan_allocator.cc +++ b/libsanitizer/asan/asan_allocator.cc @@ -263,9 +263,9 @@ struct Allocator { atomic_store(&max_redzone, options.max_redzone, memory_order_release); } - void Initialize(const AllocatorOptions &options) { + void InitLinkerInitialized(const AllocatorOptions &options) { SetAllocatorMayReturnNull(options.may_return_null); - allocator.Init(options.release_to_os_interval_ms); + allocator.InitLinkerInitialized(options.release_to_os_interval_ms); SharedInitCode(options); } @@ -741,7 +741,7 @@ StackTrace AsanChunkView::GetFreeStack() { } void InitializeAllocator(const AllocatorOptions &options) { - instance.Initialize(options); + instance.InitLinkerInitialized(options); } void ReInitializeAllocator(const AllocatorOptions &options) { -- 2.7.4 From 89fa5578505d28a6ed9b3c5f5d4a7810eb498ca0 Mon Sep 17 00:00:00 2001 From: Denis Khalikov Date: Wed, 29 Nov 2017 14:21:07 +0300 Subject: [PATCH 15/16] [ASan] Add FORTIFY_SOURCE interceptors List of interceptors: __strcat_chk, __strncat_chk, __strcpy_chk, __strncpy_chk. __poll_chk, __ppoll_chk, __read_chk, __recv_chk, __recvfrom_chk, __pread_chk, __pread64_chk. __getgroups_chk, __getcwd_chk, __realpath_chk, __confstr_chk. __mbstowcs_chk, __mbsrtowcs_chk, __mbsnrtowcs_chk, __wcstombs_chk, __wcsrtombs_chk, __wcsnrtombs_chk, __wcrtomb_chk, __ttyname_r_chk. Change-Id: I16ef4d2d80089b96e5c51ea5160b706b35f9ce18 --- .../c-c++-common/asan/strcat-overflow-fortify-1.c | 30 ++ .../c-c++-common/asan/strcpy-overflow-fortify-1.c | 28 ++ .../c-c++-common/asan/strncat-overflow-fortify-1.c | 31 ++ .../c-c++-common/asan/strncpy-overflow-fortify-1.c | 30 ++ gcc/testsuite/gcc.dg/asan/confstr-fortify-1.c | 30 ++ gcc/testsuite/gcc.dg/asan/getcwd-fortify-1.c | 30 ++ gcc/testsuite/gcc.dg/asan/mbsrtowcs-fortify-1.c | 33 ++ gcc/testsuite/gcc.dg/asan/mbstowcs-fortify-1.c | 29 ++ gcc/testsuite/gcc.dg/asan/realpath-fortify-1.c | 31 ++ gcc/testsuite/gcc.dg/asan/wcrtomb-fortify-1.c | 35 ++ gcc/testsuite/gcc.dg/asan/wcsrtombs-fortify-1.c | 35 ++ gcc/testsuite/gcc.dg/asan/wcstombs-fortify-1.c | 33 ++ libsanitizer/asan/asan_interceptors.cc | 87 +++++ libsanitizer/asan/asan_interceptors.h | 6 + .../sanitizer_common_interceptors.inc | 400 ++++++++++++++++++++- .../sanitizer_platform_interceptors.h | 2 + 16 files changed, 869 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/c-c++-common/asan/strcat-overflow-fortify-1.c create mode 100644 gcc/testsuite/c-c++-common/asan/strcpy-overflow-fortify-1.c create mode 100644 gcc/testsuite/c-c++-common/asan/strncat-overflow-fortify-1.c create mode 100644 gcc/testsuite/c-c++-common/asan/strncpy-overflow-fortify-1.c create mode 100644 gcc/testsuite/gcc.dg/asan/confstr-fortify-1.c create mode 100644 gcc/testsuite/gcc.dg/asan/getcwd-fortify-1.c create mode 100644 gcc/testsuite/gcc.dg/asan/mbsrtowcs-fortify-1.c create mode 100644 gcc/testsuite/gcc.dg/asan/mbstowcs-fortify-1.c create mode 100644 gcc/testsuite/gcc.dg/asan/realpath-fortify-1.c create mode 100644 gcc/testsuite/gcc.dg/asan/wcrtomb-fortify-1.c create mode 100644 gcc/testsuite/gcc.dg/asan/wcsrtombs-fortify-1.c create mode 100644 gcc/testsuite/gcc.dg/asan/wcstombs-fortify-1.c diff --git a/gcc/testsuite/c-c++-common/asan/strcat-overflow-fortify-1.c b/gcc/testsuite/c-c++-common/asan/strcat-overflow-fortify-1.c new file mode 100644 index 0000000..06e8f47 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/strcat-overflow-fortify-1.c @@ -0,0 +1,30 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + char *hello = (char*)malloc(6); + strcpy(hello, "hello"); + hello[5] = '\0'; + strcat(hello, "world"); /* BOOM */ + return hello[0]; +} +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__strcat_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/gcc/testsuite/c-c++-common/asan/strcpy-overflow-fortify-1.c b/gcc/testsuite/c-c++-common/asan/strcpy-overflow-fortify-1.c new file mode 100644 index 0000000..e26ecf5 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/strcpy-overflow-fortify-1.c @@ -0,0 +1,28 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + char *short_buffer = (char*)malloc(3); + strcpy(short_buffer, "hello"); /* BOOM */ + return short_buffer[0]; +} +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__strcpy_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/gcc/testsuite/c-c++-common/asan/strncat-overflow-fortify-1.c b/gcc/testsuite/c-c++-common/asan/strncat-overflow-fortify-1.c new file mode 100644 index 0000000..601b701 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/strncat-overflow-fortify-1.c @@ -0,0 +1,31 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + char *hello = (char*)malloc(6); + strcpy(hello, "hello"); + strncat(hello, "world", 10); /* BOOM */ + return hello[0]; +} +// Compiler could optimize strncat under -O1 or above level of optimization but +// FORTIFY_SOURCE option enables only on that level. +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__str*cat_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/gcc/testsuite/c-c++-common/asan/strncpy-overflow-fortify-1.c b/gcc/testsuite/c-c++-common/asan/strncpy-overflow-fortify-1.c new file mode 100644 index 0000000..c02ba8d --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/strncpy-overflow-fortify-1.c @@ -0,0 +1,30 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + char *hello = (char*)malloc(6); + strcpy(hello, "hello"); + char *short_buffer = (char*)malloc(9); + strncpy(short_buffer, hello, 10); /* BOOM */ + return short_buffer[0]; +} +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__strncpy_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/gcc/testsuite/gcc.dg/asan/confstr-fortify-1.c b/gcc/testsuite/gcc.dg/asan/confstr-fortify-1.c new file mode 100644 index 0000000..5ec3099 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asan/confstr-fortify-1.c @@ -0,0 +1,30 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + char *short_buffer = (char*)malloc(3); + confstr (_CS_PATH, short_buffer, 10); + return short_buffer[0]; +} +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__confstr_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/gcc/testsuite/gcc.dg/asan/getcwd-fortify-1.c b/gcc/testsuite/gcc.dg/asan/getcwd-fortify-1.c new file mode 100644 index 0000000..b6b9973 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asan/getcwd-fortify-1.c @@ -0,0 +1,30 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + char *short_buffer = (char*)malloc(3); + getcwd (short_buffer, 4096); + return short_buffer[0]; +} +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__getcwd_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/gcc/testsuite/gcc.dg/asan/mbsrtowcs-fortify-1.c b/gcc/testsuite/gcc.dg/asan/mbsrtowcs-fortify-1.c new file mode 100644 index 0000000..224f360 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asan/mbsrtowcs-fortify-1.c @@ -0,0 +1,33 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + wchar_t *short_buffer = (wchar_t *)malloc(sizeof (wchar_t)); + mbstate_t ps; + const char *string = "long string"; + memset (&ps, 0, sizeof (mbstate_t)); + mbsrtowcs (short_buffer, &string, 10, &ps); + return short_buffer[0]; +} +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__mbsrtowcs_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/gcc/testsuite/gcc.dg/asan/mbstowcs-fortify-1.c b/gcc/testsuite/gcc.dg/asan/mbstowcs-fortify-1.c new file mode 100644 index 0000000..a43c7f2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asan/mbstowcs-fortify-1.c @@ -0,0 +1,29 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + wchar_t *short_buffer = (wchar_t *)malloc(sizeof (wchar_t)); + mbstowcs (short_buffer, "long string ", 10); + return short_buffer[0]; +} +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__mbstowcs_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/gcc/testsuite/gcc.dg/asan/realpath-fortify-1.c b/gcc/testsuite/gcc.dg/asan/realpath-fortify-1.c new file mode 100644 index 0000000..e70e0df --- /dev/null +++ b/gcc/testsuite/gcc.dg/asan/realpath-fortify-1.c @@ -0,0 +1,31 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + char *short_buffer = (char*)malloc(3); + realpath ("../", short_buffer); + return short_buffer[0]; +} +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__realpath_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/gcc/testsuite/gcc.dg/asan/wcrtomb-fortify-1.c b/gcc/testsuite/gcc.dg/asan/wcrtomb-fortify-1.c new file mode 100644 index 0000000..8a34466 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asan/wcrtomb-fortify-1.c @@ -0,0 +1,35 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + char *short_buffer = (char *) malloc (sizeof (char)); + mbstate_t ps; + memset (&ps, 0, sizeof (ps)); + const wchar_t *string = L"ABC"; + setlocale(LC_ALL, "en_US.UTF-8"); + size_t result = wcrtomb (short_buffer, 0xff40, &ps); + return result; +} +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__wcrtomb_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/gcc/testsuite/gcc.dg/asan/wcsrtombs-fortify-1.c b/gcc/testsuite/gcc.dg/asan/wcsrtombs-fortify-1.c new file mode 100644 index 0000000..77e60a97 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asan/wcsrtombs-fortify-1.c @@ -0,0 +1,35 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + char *short_buffer = (char *) malloc (sizeof (char)); + const wchar_t *string = L"ABC"; + mbstate_t ps; + memset (&ps, 0, sizeof (mbstate_t)); + setlocale(LC_ALL, "en_US.UTF-8"); + size_t result = wcsrtombs (short_buffer, &string, 4096, &ps); + return result; +} +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__wcsrtombs_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/gcc/testsuite/gcc.dg/asan/wcstombs-fortify-1.c b/gcc/testsuite/gcc.dg/asan/wcstombs-fortify-1.c new file mode 100644 index 0000000..d50e5fa --- /dev/null +++ b/gcc/testsuite/gcc.dg/asan/wcstombs-fortify-1.c @@ -0,0 +1,33 @@ +/* { dg-do run } */ +/* { dg-options "-D_FORTIFY_SOURCE=1 -w" } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O1" } } */ +/* { dg-shouldfail "asan" } */ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * +__asan_default_options () { + return "fast_unwind_on_malloc=false"; +} + +#ifdef __cplusplus +} +#endif + +int main(int argc, char **argv) { + char *short_buffer = (char *) malloc (sizeof (char)); + const wchar_t *string = L"ABC"; + setlocale(LC_ALL, "en_US.UTF-8"); + size_t result = wcstombs (short_buffer, string, 4096); + return result; +} +/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)__wcstombs_chk|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ + diff --git a/libsanitizer/asan/asan_interceptors.cc b/libsanitizer/asan/asan_interceptors.cc index 49c9fef..1678372 100644 --- a/libsanitizer/asan/asan_interceptors.cc +++ b/libsanitizer/asan/asan_interceptors.cc @@ -611,6 +611,85 @@ INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { return REAL(strncpy)(to, from, size); } +#if ASAN_INTERCEPT__FORTIFY_SOURCE +INTERCEPTOR(char*, __strcpy_chk, char *to, const char *from, + uptr to_size) { // NOLINT + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, __strcpy_chk); // NOLINT +#if SANITIZER_MAC + if (UNLIKELY(!asan_inited)) return REAL(strcpy)(to, from); // NOLINT +#endif + // strcpy is called from malloc_default_purgeable_zone() + // in __asan::ReplaceSystemAlloc() on Mac. + if (asan_init_is_running) { + return REAL(strcpy)(to, from); // NOLINT + } + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_size = REAL(strlen)(from) + 1; + CHECK_RANGES_OVERLAP("__strcpy_chk", to, to_size, from, from_size); + ASAN_READ_RANGE(ctx, from, from_size); + ASAN_WRITE_RANGE(ctx, to, from_size); + } + return REAL(strcpy)(to, from); // NOLINT +} + +INTERCEPTOR(char*, __strncpy_chk, char *to, const char *from, uptr size, + uptr to_size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, __strncpy_chk); + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1); + CHECK_RANGES_OVERLAP("__strncpy_chk", to, to_size, from, from_size); + ASAN_READ_RANGE(ctx, from, from_size); + ASAN_WRITE_RANGE(ctx, to, size); + } + return REAL(strncpy)(to, from, size); +} + +INTERCEPTOR(char*, __strcat_chk, char *to, const char *from, uptr to_size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, __strcat_chk); // NOLINT + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_length = REAL(strlen)(from); + ASAN_READ_RANGE(ctx, from, from_length + 1); + uptr to_length = REAL(strlen)(to); + ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); + ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); + // If the copying actually happens, the |from| string should not overlap + // with the resulting string starting at |to|, which has a length of + // to_length + from_length + 1. + if (from_length > 0) { + CHECK_RANGES_OVERLAP("__strcat_chk", to, to_size, + from, from_length + 1); + } + } + return REAL(strcat)(to, from); // NOLINT +} + +INTERCEPTOR(char*, __strncat_chk, char *to, const char *from, uptr size, + uptr to_size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, __strncat_chk); + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_length = MaybeRealStrnlen(from, size); + uptr copy_length = Min(size, from_length + 1); + ASAN_READ_RANGE(ctx, from, copy_length); + uptr to_length = REAL(strlen)(to); + ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); + ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); + if (from_length > 0) { + CHECK_RANGES_OVERLAP("__strncat_chk", to, to_size, + from, copy_length); + } + } + return REAL(strncat)(to, from, size); +} +#endif //ASAN_INTERCEPT__FORTIFY_SOURCE + INTERCEPTOR(long, strtol, const char *nptr, // NOLINT char **endptr, int base) { void *ctx; @@ -747,6 +826,14 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(strncat); ASAN_INTERCEPT_FUNC(strncpy); ASAN_INTERCEPT_FUNC(strdup); + +#if ASAN_INTERCEPT__FORTIFY_SOURCE + ASAN_INTERCEPT_FUNC(__strcat_chk); + ASAN_INTERCEPT_FUNC(__strncat_chk); + ASAN_INTERCEPT_FUNC(__strcpy_chk); + ASAN_INTERCEPT_FUNC(__strncpy_chk); +#endif + #if ASAN_INTERCEPT___STRDUP ASAN_INTERCEPT_FUNC(__strdup); #endif diff --git a/libsanitizer/asan/asan_interceptors.h b/libsanitizer/asan/asan_interceptors.h index 565a632..706729c 100644 --- a/libsanitizer/asan/asan_interceptors.h +++ b/libsanitizer/asan/asan_interceptors.h @@ -62,6 +62,12 @@ # define ASAN_INTERCEPT___LONGJMP_CHK 0 #endif +#if SANITIZER_LINUX && !SANITIZER_ANDROID +# define ASAN_INTERCEPT__FORTIFY_SOURCE 1 +#else +# define ASAN_INTERCEPT__FORTIFY_SOURCE 0 +#endif + // Android bug: https://code.google.com/p/android/issues/detail?id=61799 #if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS && \ !(SANITIZER_ANDROID && defined(__i386)) diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc index c6f1ae0..19879b6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc @@ -778,6 +778,25 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { #define INIT_READ #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(SSIZE_T, __read_chk, int fd, void *ptr, SIZE_T count, + SIZE_T bufflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __read_chk, fd, ptr, count, bufflen); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SSIZE_T res = REAL(read)(fd, ptr, count); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT___READ_CHK COMMON_INTERCEPT_FUNCTION(__read_chk) +#else +#define INIT___READ_CHK +#endif + #if SANITIZER_INTERCEPT_PREAD INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; @@ -796,6 +815,25 @@ INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { #define INIT_PREAD #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(SSIZE_T, __pread_chk, int fd, void *ptr, SIZE_T count, + OFF_T offset, SIZE_T buflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __pread_chk, fd, ptr, count, offset, buflen); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SSIZE_T res = REAL(pread)(fd, ptr, count, offset); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT___PREAD_CHK COMMON_INTERCEPT_FUNCTION(__pread_chk) +#else +#define INIT___PREAD_CHK +#endif + #if SANITIZER_INTERCEPT_PREAD64 INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { void *ctx; @@ -814,6 +852,26 @@ INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { #define INIT_PREAD64 #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(SSIZE_T, __pread64_chk, int fd, void *ptr, SIZE_T count, + OFF64_T offset, SIZE_T buflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __pread64_chk, fd, ptr, count, offset, + buflen); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SSIZE_T res = REAL(pread64)(fd, ptr, count, offset); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT___PREAD64_CHK COMMON_INTERCEPT_FUNCTION(__pread64_chk) +#else +#define INIT___PREAD64_CHK +#endif + #if SANITIZER_INTERCEPT_READV INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov, int iovcnt) { @@ -2876,6 +2934,22 @@ INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { #define INIT_GETCWD #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(char *, __getcwd_chk, char *buf, SIZE_T size, SIZE_T buflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __getcwd_chk, buf, size, buflen); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + char *res = REAL(getcwd)(buf, size); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT___GETCWD_CHK COMMON_INTERCEPT_FUNCTION(__getcwd_chk); +#else +#define INIT___GETCWD_CHK +#endif + #if SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME INTERCEPTOR(char *, get_current_dir_name, int fake) { void *ctx; @@ -2998,6 +3072,48 @@ INTERCEPTOR(SIZE_T, mbsrtowcs, wchar_t *dest, const char **src, SIZE_T len, #define INIT_MBSTOWCS #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(SIZE_T, __mbstowcs_chk, wchar_t *dest, const char *src, + SIZE_T len, SIZE_T destlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __mbstowcs_chk, dest, src, len, destlen); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SIZE_T res = REAL(mbstowcs)(dest, src, len); + if (res != (SIZE_T) - 1 && dest) { + SIZE_T write_cnt = res + (res < len); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +INTERCEPTOR(SIZE_T, __mbsrtowcs_chk, wchar_t *dest, const char **src, + SIZE_T len, void *ps, SIZE_T destlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __mbsrtowcs_chk, dest, src, len, ps, destlen); + if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SIZE_T res = REAL(mbsrtowcs)(dest, src, len, ps); + if (res != (SIZE_T)(-1) && dest && src) { + // This function, and several others, may or may not write the terminating + // \0 character. They write it iff they clear *src. + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +#define INIT___MBSTOWCS_CHK \ + COMMON_INTERCEPT_FUNCTION(__mbstowcs_chk); \ + COMMON_INTERCEPT_FUNCTION(__mbsrtowcs_chk); +#else +#define INIT___MBSTOWCS_CHK +#endif + #if SANITIZER_INTERCEPT_MBSNRTOWCS INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms, SIZE_T len, void *ps) { @@ -3024,6 +3140,33 @@ INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms, #define INIT_MBSNRTOWCS #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(SIZE_T, __mbsnrtowcs_chk, wchar_t *dest, const char **src, + SIZE_T nms, SIZE_T len, void *ps, SIZE_T destlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __mbsnrtowcs_chk, dest, src, nms, len, ps, + destlen); + if (src) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); + } + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SIZE_T res = REAL(mbsnrtowcs)(dest, src, nms, len, ps); + if (res != (SIZE_T)(-1) && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +#define INIT___MBSNRTOWCS_CHK COMMON_INTERCEPT_FUNCTION(__mbsnrtowcs_chk); +#else +#define INIT___MBSNRTOWCS_CHK +#endif + #if SANITIZER_INTERCEPT_WCSTOMBS INTERCEPTOR(SIZE_T, wcstombs, char *dest, const wchar_t *src, SIZE_T len) { void *ctx; @@ -3063,6 +3206,47 @@ INTERCEPTOR(SIZE_T, wcsrtombs, char *dest, const wchar_t **src, SIZE_T len, #define INIT_WCSTOMBS #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(SIZE_T, __wcstombs_chk, char *dest, const wchar_t *src, + SIZE_T len, SIZE_T destlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __wcstombs_chk, dest, src, len, destlen); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SIZE_T res = REAL(wcstombs)(dest, src, len); + if (res != (SIZE_T) - 1 && dest) { + SIZE_T write_cnt = res + (res < len); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +INTERCEPTOR(SIZE_T, __wcsrtombs_chk, char *dest, const wchar_t **src, + SIZE_T len, void *ps, SIZE_T destlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __wcsrtombs_chk, dest, src, len, ps, + destlen); + if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SIZE_T res = REAL(wcsrtombs)(dest, src, len, ps); + if (res != (SIZE_T) - 1 && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +#define INIT___WCSTOMBS_CHK \ + COMMON_INTERCEPT_FUNCTION(__wcstombs_chk); \ + COMMON_INTERCEPT_FUNCTION(__wcsrtombs_chk); +#else +#define INIT___WCSTOMBS_CHK +#endif + #if SANITIZER_INTERCEPT_WCSNRTOMBS INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms, SIZE_T len, void *ps) { @@ -3089,6 +3273,32 @@ INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms, #define INIT_WCSNRTOMBS #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(SIZE_T, __wcsnrtombs_chk, char *dest, const wchar_t **src, + SIZE_T nms, SIZE_T len, void *ps, SIZE_T destlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __wcsnrtombs_chk, dest, src, nms, len, ps, + destlen); + if (src) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); + } + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps); + if (res != ((SIZE_T)-1) && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +#define INIT___WCSNRTOMBS_CHK COMMON_INTERCEPT_FUNCTION(__wcsnrtombs_chk); +#else +#define INIT___WCSNRTOMBS_CHK +#endif #if SANITIZER_INTERCEPT_WCRTOMB INTERCEPTOR(SIZE_T, wcrtomb, char *dest, wchar_t src, void *ps) { @@ -3111,6 +3321,28 @@ INTERCEPTOR(SIZE_T, wcrtomb, char *dest, wchar_t src, void *ps) { #define INIT_WCRTOMB #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(SIZE_T, __wcrtomb_chk, char *dest, wchar_t src, void *ps, + SIZE_T destlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __wcrtomb_chk, dest, src, ps, destlen); + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SIZE_T res = REAL(wcrtomb)(dest, src, ps); + if (res != ((SIZE_T)-1) && dest) { + SIZE_T write_cnt = res; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +#define INIT___WCRTOMB_CHK COMMON_INTERCEPT_FUNCTION(__wcrtomb_chk); +#else +#define INIT___WCRTOMB_CHK +#endif + #if SANITIZER_INTERCEPT_TCGETATTR INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) { void *ctx; @@ -3153,6 +3385,32 @@ INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) { #define INIT_REALPATH #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(char *, __realpath_chk, const char *path, char *resolved_path, + SIZE_T resolvedlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __realpath_chk, path, resolved_path, + resolvedlen); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + + // Workaround a bug in glibc where dlsym(RTLD_NEXT, ...) returns the oldest + // version of a versioned symbol. For realpath(), this gives us something + // (called __old_realpath) that does not handle NULL in the second argument. + // Handle it as part of the interceptor. + char *allocated_path = nullptr; + if (!resolved_path) + allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1); + + char *res = REAL(realpath)(path, resolved_path); + if (allocated_path && !res) WRAP(free)(allocated_path); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT___REALPATH_CHK COMMON_INTERCEPT_FUNCTION(__realpath_chk); +#else +#define INIT___REALPATH_CHK +#endif + #if SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME INTERCEPTOR(char *, canonicalize_file_name, const char *path) { void *ctx; @@ -3185,6 +3443,24 @@ INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) { #define INIT_CONFSTR #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(SIZE_T, __confstr_chk, int name, char *buf, SIZE_T len, + SIZE_T bufflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __confstr_chk, name, buf, len, bufflen); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SIZE_T res = REAL(confstr)(name, buf, len); + if (buf && res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len); + return res; +} +#define INIT___CONFSTR_CHK COMMON_INTERCEPT_FUNCTION(__confstr_chk); +#else +#define INIT___CONFSTR_CHK +#endif + #if SANITIZER_INTERCEPT_SCHED_GETAFFINITY INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) { void *ctx; @@ -3399,6 +3675,23 @@ INTERCEPTOR(int, getgroups, int size, u32 *lst) { #define INIT_GETGROUPS #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(int, __getgroups_chk, int size, u32 *lst, SIZE_T lstsize) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __getgroups_chk, size, lst, lstsize); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + int res = REAL(getgroups)(size, lst); + if (res >= 0 && lst && size > 0) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); + return res; +} +#define INIT___GETGROUPS_CHK COMMON_INTERCEPT_FUNCTION(__getgroups_chk); +#else +#define INIT___GETGROUPS_CHK +#endif + #if SANITIZER_INTERCEPT_POLL static void read_pollfd(void *ctx, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds) { @@ -3448,6 +3741,42 @@ INTERCEPTOR(int, ppoll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, #define INIT_PPOLL #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(int, __poll_chk, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, + int timeout, SIZE_T fdslen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __poll_chk, fds, nfds, timeout, fdslen); + if (fds && nfds) read_pollfd(ctx, fds, nfds); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(poll)(fds, nfds, timeout); + if (fds && nfds) write_pollfd(ctx, fds, nfds); + return res; +} +#define INIT___POLL_CHK COMMON_INTERCEPT_FUNCTION(__poll_chk); +#else +#define INIT___POLL_CHK +#endif + +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(int, __ppoll_chk, __sanitizer_pollfd *fds, + __sanitizer_nfds_t nfds, void *timeout_ts, + __sanitizer_sigset_t *sigmask, SIZE_T fdslen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __ppoll_chk, fds, nfds, timeout_ts, + sigmask, fdslen); + if (fds && nfds) read_pollfd(ctx, fds, nfds); + if (timeout_ts) + COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout_ts, struct_timespec_sz); + // FIXME: read sigmask when all of sigemptyset, etc are intercepted. + int res = + COMMON_INTERCEPTOR_BLOCK_REAL(ppoll)(fds, nfds, timeout_ts, sigmask); + if (fds && nfds) write_pollfd(ctx, fds, nfds); + return res; +} +#define INIT___PPOLL_CHK COMMON_INTERCEPT_FUNCTION(__ppoll_chk); +#else +#define INIT___PPOLL_CHK +#endif + #if SANITIZER_INTERCEPT_WORDEXP INTERCEPTOR(int, wordexp, char *s, __sanitizer_wordexp_t *p, int flags) { void *ctx; @@ -4236,6 +4565,21 @@ INTERCEPTOR(int, ttyname_r, int fd, char *name, SIZE_T namesize) { #define INIT_TTYNAME_R #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(int, __ttyname_r_chk, int fd, char *name, SIZE_T namesize, + SIZE_T namelen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __ttyname_r_chk, fd, name, namesize, namelen); + int res = REAL(ttyname_r)(fd, name, namesize); + if (res == 0) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, namelen + 1); + return res; +} +#define INIT___TTYNAME_R_CHK COMMON_INTERCEPT_FUNCTION(__ttyname_r_chk); +#else +#define INIT___TTYNAME_R_CHK +#endif + #if SANITIZER_INTERCEPT_TEMPNAM INTERCEPTOR(char *, tempnam, char *dir, char *pfx) { void *ctx; @@ -5771,6 +6115,45 @@ INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags, #define INIT_RECV_RECVFROM #endif +#if SANITIZER_INTERCEPT_FORTIFY_SOURCE +INTERCEPTOR(SSIZE_T, __recv_chk, int fd, void *buf, SIZE_T len, SIZE_T buflen, + int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __recv_chk, fd, buf, len, buflen, flags); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(recv)(fd, buf, len, flags); + if (res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len)); + } + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} + +INTERCEPTOR(SSIZE_T, __recvfrom_chk, int fd, void *buf, SIZE_T len, + SIZE_T buflen, int flags, void *srcaddr, int *addrlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __recvfrom_chk, fd, buf, len, buflen, flags, + srcaddr, addrlen); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SIZE_T srcaddr_sz; + if (srcaddr) srcaddr_sz = *addrlen; + (void)srcaddr_sz; // prevent "set but not used" warning + SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen); + if (res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len)); + if (srcaddr) + COMMON_INTERCEPTOR_INITIALIZE_RANGE(srcaddr, + Min((SIZE_T)*addrlen, srcaddr_sz)); + } + return res; +} +#define INIT___RECV_RECVFROM_CHK \ + COMMON_INTERCEPT_FUNCTION(__recv_chk); \ + COMMON_INTERCEPT_FUNCTION(__recvfrom_chk); +#else +#define INIT___RECV_RECVFROM_CHK +#endif + #if SANITIZER_INTERCEPT_SEND_SENDTO INTERCEPTOR(SSIZE_T, send, int fd, void *buf, SIZE_T len, int flags) { void *ctx; @@ -6133,7 +6516,22 @@ static void InitializeCommonInterceptors() { INIT___XSTAT64; INIT___LXSTAT; INIT___LXSTAT64; - // FIXME: add other *stat interceptors. + INIT___READ_CHK; + INIT___POLL_CHK; + INIT___PPOLL_CHK; + INIT___RECV_RECVFROM_CHK; + INIT___PREAD_CHK; + INIT___PREAD64_CHK; + INIT___CONFSTR_CHK; + INIT___GETCWD_CHK; + INIT___GETGROUPS_CHK; + INIT___REALPATH_CHK; + INIT___MBSTOWCS_CHK; + INIT___MBSNRTOWCS_CHK; + INIT___WCSTOMBS_CHK; + INIT___WCSNRTOMBS_CHK; + INIT___WCRTOMB_CHK; + INIT___TTYNAME_R_CHK; INIT___PRINTF_CHK; INIT_MCHECK; INIT_MCHECK_PEDANTIC; diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h index 0ee5ec2..f165110 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h @@ -119,6 +119,8 @@ #define SANITIZER_INTERCEPT___PRINTF_CHK \ (SANITIZER_INTERCEPT_PRINTF && SI_LINUX_NOT_ANDROID) +#define SANITIZER_INTERCEPT_FORTIFY_SOURCE SI_LINUX_NOT_ANDROID + #define SANITIZER_INTERCEPT_FREXP 1 #define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_NOT_WINDOWS -- 2.7.4 From 6cb8bbb4a6f6a7812068ca160a39cd535141e873 Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov Date: Thu, 17 May 2018 16:32:19 +0300 Subject: [PATCH 16/16] packaging: Add -Wformat options to disable warnings and tests FAIL. "-Wformat-..." options generate warnings like: cc1: warning: -Wformat-security ignored without -Wformat [-Wformat-security] that makes a lot of unexpected failures in testsuite while checking produced output (FAIL: .. test for excess errors) and also results comparison is messed up for the new tests. Insert -Wformat if it's missing. Change-Id: Ia61f256b1f8c3fe5b3ee3c02d363eb901c68adbe --- packaging/gcc-aarch64.spec | 11 +++++++++++ packaging/gcc-armv7hl.spec | 11 +++++++++++ packaging/gcc-armv7l.spec | 11 +++++++++++ packaging/linaro-gcc.spec | 11 +++++++++++ 4 files changed, 44 insertions(+) diff --git a/packaging/gcc-aarch64.spec b/packaging/gcc-aarch64.spec index a04e5f8..de57a4a 100644 --- a/packaging/gcc-aarch64.spec +++ b/packaging/gcc-aarch64.spec @@ -696,6 +696,17 @@ RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/-m\(arch\|tune\|cpu\)=[^ ]*//g'` RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/-m\(sse\|fpmath\)[^ ]*//g'` } RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/ */ /g'` + +# -Wall is stripped off now, so -Wformat will not turn on implicitly for +# "-Wformat-.." option group, causing additional build warnings and testsuite +# FAIL ("test for excess errors" type). +# Insert "-Wformat" if we find "-Wformat-..." +if [ ! -z "$(echo $RPM_OPT_FLAGS | grep -o "\B\-Wformat\-")" ]; then + if [ -z "$(echo $RPM_OPT_FLAGS | grep -Po "\B\-Wformat(\s|\Z)")" ]; then + RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS | sed -e "s/-Wformat-/-Wformat -Wformat-/") + fi +fi + %{?asan: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -DASAN_INIT_FIRST) } %ifarch armv7l armv7hl aarch64 diff --git a/packaging/gcc-armv7hl.spec b/packaging/gcc-armv7hl.spec index 49f6edf..c72d024 100644 --- a/packaging/gcc-armv7hl.spec +++ b/packaging/gcc-armv7hl.spec @@ -696,6 +696,17 @@ RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/-m\(arch\|tune\|cpu\)=[^ ]*//g'` RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/-m\(sse\|fpmath\)[^ ]*//g'` } RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/ */ /g'` + +# -Wall is stripped off now, so -Wformat will not turn on implicitly for +# "-Wformat-.." option group, causing additional build warnings and testsuite +# FAIL ("test for excess errors" type). +# Insert "-Wformat" if we find "-Wformat-..." +if [ ! -z "$(echo $RPM_OPT_FLAGS | grep -o "\B\-Wformat\-")" ]; then + if [ -z "$(echo $RPM_OPT_FLAGS | grep -Po "\B\-Wformat(\s|\Z)")" ]; then + RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS | sed -e "s/-Wformat-/-Wformat -Wformat-/") + fi +fi + %{?asan: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -DASAN_INIT_FIRST) } %ifarch armv7l armv7hl aarch64 diff --git a/packaging/gcc-armv7l.spec b/packaging/gcc-armv7l.spec index 95976e3..6a8604d 100644 --- a/packaging/gcc-armv7l.spec +++ b/packaging/gcc-armv7l.spec @@ -696,6 +696,17 @@ RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/-m\(arch\|tune\|cpu\)=[^ ]*//g'` RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/-m\(sse\|fpmath\)[^ ]*//g'` } RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/ */ /g'` + +# -Wall is stripped off now, so -Wformat will not turn on implicitly for +# "-Wformat-.." option group, causing additional build warnings and testsuite +# FAIL ("test for excess errors" type). +# Insert "-Wformat" if we find "-Wformat-..." +if [ ! -z "$(echo $RPM_OPT_FLAGS | grep -o "\B\-Wformat\-")" ]; then + if [ -z "$(echo $RPM_OPT_FLAGS | grep -Po "\B\-Wformat(\s|\Z)")" ]; then + RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS | sed -e "s/-Wformat-/-Wformat -Wformat-/") + fi +fi + %{?asan: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -DASAN_INIT_FIRST) } %ifarch armv7l armv7hl aarch64 diff --git a/packaging/linaro-gcc.spec b/packaging/linaro-gcc.spec index fd960c5..3dd714e 100644 --- a/packaging/linaro-gcc.spec +++ b/packaging/linaro-gcc.spec @@ -693,6 +693,17 @@ RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/-m\(arch\|tune\|cpu\)=[^ ]*//g'` RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/-m\(sse\|fpmath\)[^ ]*//g'` } RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS|sed -e 's/ */ /g'` + +# -Wall is stripped off now, so -Wformat will not turn on implicitly for +# "-Wformat-.." option group, causing additional build warnings and testsuite +# FAIL ("test for excess errors" type). +# Insert "-Wformat" if we find "-Wformat-..." +if [ ! -z "$(echo $RPM_OPT_FLAGS | grep -o "\B\-Wformat\-")" ]; then + if [ -z "$(echo $RPM_OPT_FLAGS | grep -Po "\B\-Wformat(\s|\Z)")" ]; then + RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS | sed -e "s/-Wformat-/-Wformat -Wformat-/") + fi +fi + %{?asan: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -DASAN_INIT_FIRST) } %ifarch armv7l armv7hl aarch64 -- 2.7.4