From 070b3cd5223758d8f65eeb75bcfdcaeed1b19106 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 31 Jul 2018 20:51:06 +0000 Subject: [PATCH 01/16] * targhooks.c (default_have_speculation_safe_value): Add ATTRIBUTE_UNUSED. (backported 30929ff6997501127e0d09e4917eef3f42d33899) Change-Id: I59da0b5a1f07516058eacb291a436e80fc2d52d5 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@263180 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 5 +++++ gcc/targhooks.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 50fe0d4..d8ad496 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2018-07-31 Ian Lance Taylor + + * targhooks.c (default_have_speculation_safe_value): Add + ATTRIBUTE_UNUSED. + 2018-07-31 Richard Earnshaw * targhooks.h (speculation_safe_value_not_needed): New prototype. diff --git a/gcc/targhooks.c b/gcc/targhooks.c index 58c9068..23dd41f 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -1967,7 +1967,7 @@ default_optab_supported_p (int, machine_mode, machine_mode, optimization_type) /* Default implementation of TARGET_HAVE_SPECULATION_SAFE_VALUE. */ bool -default_have_speculation_safe_value (bool active) +default_have_speculation_safe_value (bool active ATTRIBUTE_UNUSED) { #ifdef HAVE_speculation_barrier return active ? HAVE_speculation_barrier : true; -- 2.7.4 From fa5fafbf45197bb30ddc50f477d0c8af409d413f Mon Sep 17 00:00:00 2001 From: rearnsha Date: Wed, 1 Aug 2018 08:16:38 +0000 Subject: [PATCH 02/16] x86 - add speculation_barrier pattern This patch adds a speculation barrier for x86, based on my understanding of the required mitigation for that CPU, which is to use an lfence instruction. This patch needs some review by an x86 expert and if adjustments are needed, I'd appreciate it if they could be picked up by the port maintainer. This is supposed to serve as an example of how to deploy the new __builtin_speculation_safe_value() intrinsic on this architecture. * config/i386/i386.md (unspecv): Add UNSPECV_SPECULATION_BARRIER. (speculation_barrier): New insn. (backported 5812770a029b1a49b72cabf08b2f202d2d00dc69) Change-Id: I94d1aacb04c9288a0fbeb38109aeb80a59ca47e6 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@263196 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 5 +++++ gcc/config/i386/i386.md | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d8ad496..b5383da 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2018-08-01 Richard Earnshaw + + * config/i386/i386.md (unspecv): Add UNSPECV_SPECULATION_BARRIER. + (speculation_barrier): New insn. + 2018-07-31 Ian Lance Taylor * targhooks.c (default_have_speculation_safe_value): Add diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 46987d3..593866f 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -271,6 +271,9 @@ ;; For RDPKRU and WRPKRU support UNSPECV_PKU + + ;; For Speculation Barrier support + UNSPECV_SPECULATION_BARRIER ]) ;; Constants to represent rounding modes in the ROUND instruction @@ -19634,6 +19637,13 @@ "wrpkru" [(set_attr "type" "other")]) +(define_insn "speculation_barrier" + [(unspec_volatile [(const_int 0)] UNSPECV_SPECULATION_BARRIER)] + "" + "lfence" + [(set_attr "type" "other") + (set_attr "length" "3")]) + (include "mmx.md") (include "sse.md") (include "sync.md") -- 2.7.4 From f0815b07a172fef498eddc36b5aa0c5047952419 Mon Sep 17 00:00:00 2001 From: rearnsha Date: Thu, 23 Aug 2018 09:47:34 +0000 Subject: [PATCH 03/16] PR target/86951 arm - Handle speculation barriers on pre-armv7 CPUs The AArch32 instruction sets prior to Armv7 do not define the ISB and DSB instructions that are needed to form a speculation barrier. While I do not know of any instances of cores based on those instruction sets being vulnerable to speculative side channel attacks it is possible to run code built for those ISAs on more recent hardware where they would become vulnerable. This patch works around this by using a library call added to libgcc. That code can then take any platform-specific actions necessary to ensure safety. For the moment I've only handled two cases: the library code being built for armv7 or later anyway and running on Linux. On Linux we can handle this by calling the kernel function that will flush a small amount of cache. Such a sequence ends with a ISB+DSB sequence if running on an Armv7 or later CPU. gcc: PR target/86951 * config/arm/arm-protos.h (arm_emit_speculation_barrier): New prototype. * config/arm/arm.c (speculation_barrier_libfunc): New static variable. (arm_init_libfuncs): Initialize it. (arm_emit_speculation_barrier): New function. * config/arm/arm.md (speculation_barrier): Call arm_emit_speculation_barrier for architectures that do not have DSB or ISB. (speculation_barrier_insn): Only match on Armv7 or later. libgcc: PR target/86951 * config/arm/lib1funcs.asm (speculation_barrier): New function. * config/arm/t-arm (LIB1ASMFUNCS): Add it to list of functions to build. (backported a7852b351bded1c6885e36e3ba70c298afcd1699) Change-Id: I4b77551dad3effbe49b06ff8bdd20daae69869e3 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@263806 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 14 ++++++++++++++ gcc/config/arm/arm-protos.h | 2 ++ gcc/config/arm/arm.c | 14 ++++++++++++++ gcc/config/arm/arm.md | 16 +++++++++++----- libgcc/ChangeLog | 7 +++++++ libgcc/config/arm/lib1funcs.S | 44 +++++++++++++++++++++++++++++++++++++++++++ libgcc/config/arm/t-arm | 2 +- 7 files changed, 93 insertions(+), 6 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b5383da..ea8a549 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,17 @@ +2018-08-23 Richard Earnshaw + + PR target/86951 + * config/arm/arm-protos.h (arm_emit_speculation_barrier): New + prototype. + * config/arm/arm.c (speculation_barrier_libfunc): New static + variable. + (arm_init_libfuncs): Initialize it. + (arm_emit_speculation_barrier): New function. + * config/arm/arm.md (speculation_barrier): Call + arm_emit_speculation_barrier for architectures that do not have + DSB or ISB. + (speculation_barrier_insn): Only match on Armv7 or later. + 2018-08-01 Richard Earnshaw * config/i386/i386.md (unspecv): Add UNSPECV_SPECULATION_BARRIER. diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h index 54ff5d8..3a96ca4 100644 --- a/gcc/config/arm/arm-protos.h +++ b/gcc/config/arm/arm-protos.h @@ -56,6 +56,8 @@ extern void arm_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update extern rtx arm_simd_vect_par_cnst_half (machine_mode mode, bool high); extern bool arm_simd_check_vect_par_cnst_half_p (rtx op, machine_mode mode, bool high); +extern void arm_emit_speculation_barrier_function (void); + #ifdef RTX_CODE extern void arm_gen_unlikely_cbranch (enum rtx_code, machine_mode cc_mode, rtx label_ref); diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index ae21d1a..022ae1e 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -2409,6 +2409,8 @@ arm_set_fixed_conv_libfunc (convert_optab optable, machine_mode to, set_conv_libfunc (optable, to, from, buffer); } +static GTY(()) rtx speculation_barrier_libfunc; + /* Set up library functions unique to ARM. */ static void @@ -2691,6 +2693,8 @@ arm_init_libfuncs (void) if (TARGET_AAPCS_BASED) synchronize_libfunc = init_one_libfunc ("__sync_synchronize"); + + speculation_barrier_libfunc = init_one_libfunc ("__speculation_barrier"); } /* On AAPCS systems, this is the "struct __va_list". */ @@ -30717,4 +30721,14 @@ arm_gen_unlikely_cbranch (enum rtx_code code, machine_mode cc_mode, emit_unlikely_jump (gen_rtx_SET (pc_rtx, x)); } +/* Emit a speculation barrier on target architectures that do not have + DSB/ISB directly. Such systems probably don't need a barrier + themselves, but if the code is ever run on a later architecture, it + might become a problem. */ +void +arm_emit_speculation_barrier_function () +{ + emit_library_call (speculation_barrier_libfunc, LCT_NORMAL, VOIDmode, 0); +} + #include "gt-arm.h" diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index ffdd6c2..bd2f6d8 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -11925,10 +11925,16 @@ [(unspec_volatile [(const_int 0)] VUNSPEC_SPECULATION_BARRIER)] "TARGET_EITHER" " - /* Don't emit anything for Thumb1 and suppress the warning from the - generic expansion. */ - if (!TARGET_32BIT) - DONE; + /* For thumb1 (except Armv8 derivatives), and for pre-Armv7 we don't + have a usable barrier (and probably don't need one in practice). + But to be safe if such code is run on later architectures, call a + helper function in libgcc that will do the thing for the active + system. */ + if (!(arm_arch7 || arm_arch8)) + { + arm_emit_speculation_barrier_function (); + DONE; + } " ) @@ -11936,7 +11942,7 @@ ;; tracking. (define_insn "*speculation_barrier_insn" [(unspec_volatile [(const_int 0)] VUNSPEC_SPECULATION_BARRIER)] - "TARGET_32BIT" + "arm_arch7 || arm_arch8" "isb\;dsb\\tsy" [(set_attr "type" "block") (set_attr "length" "8")] diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog index 48568ef..a5c403c 100644 --- a/libgcc/ChangeLog +++ b/libgcc/ChangeLog @@ -1,3 +1,10 @@ +2018-08-23 Richard Earnshaw + + PR target/86951 + * config/arm/lib1funcs.asm (speculation_barrier): New function. + * config/arm/t-arm (LIB1ASMFUNCS): Add it to list of functions + to build. + 2017-01-09 Andreas Tobler Backport from mainline diff --git a/libgcc/config/arm/lib1funcs.S b/libgcc/config/arm/lib1funcs.S index ba52e7b..e8fc502 100644 --- a/libgcc/config/arm/lib1funcs.S +++ b/libgcc/config/arm/lib1funcs.S @@ -1576,6 +1576,50 @@ LSYM(Lover12): #error "This is only for ARM EABI GNU/Linux" #endif #endif /* L_clear_cache */ + +#ifdef L_speculation_barrier + FUNC_START speculation_barrier +#if __ARM_ARCH >= 7 + isb + dsb sy +#elif defined __ARM_EABI__ && defined __linux__ + /* We don't have a speculation barrier directly for this + platform/architecture variant. But we can use a kernel + clear_cache service routine which will emit such instructions + if run on a later version of the architecture. We don't + really want to flush the cache, but we must give it a valid + address, so just clear pc..pc+1. */ +#if defined __thumb__ && !defined __thumb2__ + push {r7} + mov r7, #0xf + lsl r7, #16 + add r7, #2 + adr r0, . + 4 + add r1, r0, #1 + mov r2, #0 + svc 0 + pop {r7} +#else + do_push {r7} +#ifdef __ARM_ARCH_6T2__ + movw r7, #2 + movt r7, #0xf +#else + mov r7, #0xf0000 + add r7, r7, #2 +#endif + add r0, pc, #0 /* ADR. */ + add r1, r0, #1 + mov r2, #0 + svc 0 + do_pop {r7} +#endif /* Thumb1 only */ +#else +#warning "No speculation barrier defined for this platform" +#endif + RET + FUNC_END speculation_barrier +#endif /* ------------------------------------------------------------------------ */ /* Dword shift operations. */ /* All the following Dword shift variants rely on the fact that diff --git a/libgcc/config/arm/t-arm b/libgcc/config/arm/t-arm index 9e85ac0..274bf2a 100644 --- a/libgcc/config/arm/t-arm +++ b/libgcc/config/arm/t-arm @@ -1,6 +1,6 @@ LIB1ASMSRC = arm/lib1funcs.S LIB1ASMFUNCS = _thumb1_case_sqi _thumb1_case_uqi _thumb1_case_shi \ - _thumb1_case_uhi _thumb1_case_si + _thumb1_case_uhi _thumb1_case_si _speculation_barrier HAVE_CMSE:=$(findstring __ARM_FEATURE_CMSE,$(shell $(gcc_compile_bare) -dM -E - /dev/null),) -- 2.7.4 From e98f28299c49093f3100bc77c5174c7f85095f3f Mon Sep 17 00:00:00 2001 From: rearnsha Date: Fri, 2 Nov 2018 13:36:31 +0000 Subject: [PATCH 04/16] Although there's no fundamental reason why shrink wrapping and speculation tracking are incompatible, a phase-ordering requirement (we need to do speculation tracking before the final basic block clean-up) means that the shrink wrapping pass can undo some of the changes the speculation tracking pass makes. The result is that the tracking, while still safe is less comprehensive than we really want. So to keep things simple, and because the tracking code is quite expensive anyway, it seems best to just disable that pass when we are tracking speculative execution. * config/aarch64/aarch64.c (aarch64_override_options): Disable shrink-wrapping when -mtrack-speculation. (backported 3024ac6836fa5a0f8ab539a0823bfb609aef0e93) Change-Id: I894aa3562b8ddfc951e2eb33c15bd9d79c423c0f git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@265747 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 5 +++++ gcc/config/aarch64/aarch64.c | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index ea8a549..12247a1 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2018-11-02 Richard Earnshaw + + * config/aarch64/aarch64.c ((aarch64_override_options): Disable + shrink-wrapping when -mtrack-speculation. + 2018-08-23 Richard Earnshaw PR target/86951 diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index 3b55526..16cd780 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -8584,6 +8584,12 @@ aarch64_override_options (void) || (aarch64_arch_string && valid_arch)) gcc_assert (explicit_arch != aarch64_no_arch); + /* The pass to insert speculation tracking runs before + shrink-wrapping and the latter does not know how to update the + tracking status. So disable it in this case. */ + if (aarch64_track_speculation) + flag_shrink_wrap = 0; + aarch64_override_options_internal (&global_options); /* Save these options as the default ones in case we push and pop them later -- 2.7.4 From eac97ce657b0b1a3800383f327414771d26227e6 Mon Sep 17 00:00:00 2001 From: rearnsha Date: Tue, 6 Nov 2018 17:18:37 +0000 Subject: [PATCH 05/16] aarch64 - Set the mode for the unspec in speculation_tracker insn. The speculation tracker insn in my recent patch set for CVE-2017-5753 was missing a mode on the UNSPEC. Although this didn't break the build, it did cause an unnecessary warning from the MD parsing mechanism that I missed at the time. It's a trivial fix, as follows: * config/aarch64/aarch64.md (speculation_tracker): Set the mode for the UNSPEC. (backported 1ce555a208bf8e3422eadd9c1e3eb0b1b120a81b) Change-Id: I525ae85eeafc2e9f42b365957abdb7b494a286c2 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@265849 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 5 +++++ gcc/config/aarch64/aarch64.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 12247a1..6149d35 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2018-11-06 Richard Earnshaw + + * config/aarch64/aarch64.md (speculation_tracker): Set the mode for + the UNSPEC. + 2018-11-02 Richard Earnshaw * config/aarch64/aarch64.c ((aarch64_override_options): Disable diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md index 689a0a8..3335d06 100644 --- a/gcc/config/aarch64/aarch64.md +++ b/gcc/config/aarch64/aarch64.md @@ -5420,7 +5420,7 @@ ;; SPECULATION_TRACKER_REGNUM is reserved for this purpose when necessary. (define_insn "speculation_tracker" [(set (reg:DI SPECULATION_TRACKER_REGNUM) - (unspec [(reg:DI SPECULATION_TRACKER_REGNUM) (match_operand 0)] + (unspec:DI [(reg:DI SPECULATION_TRACKER_REGNUM) (match_operand 0)] UNSPEC_SPECULATION_TRACKER))] "" { -- 2.7.4 From 35faeb089167e1ed3cb8ee7808d72da3855c85f1 Mon Sep 17 00:00:00 2001 From: Dmitriy Evgenevich Gonzha Date: Wed, 14 Mar 2018 13:47:05 +0300 Subject: [PATCH 06/16] libsanitizer: re-implement heap profiler. In the previos heap profiler implementation HeapProfile instance was created every time BackgroundThread wanted to collect statistical data. This commit simply applies the singleton pattern: now there's only one HeapProfile object which can both decide wether it's time to collect the statistics and print profiling data. Signed-off-by: Andrey Drobyshev Change-Id: I8dfa245d826c52749564346a2cd50788c5d91124 --- .../c-c++-common/asan/auto_memory_profile.c | 35 +++ .../asan/auto_memory_profile_decrease.c | 37 +++ .../asan/auto_memory_profile_timestamp.c | 29 ++ libsanitizer/asan/asan_memory_profile.cc | 305 ++++++++++++++++++++- libsanitizer/sanitizer_common/sanitizer_common.cc | 4 + .../sanitizer_common/sanitizer_common_libcdep.cc | 11 +- libsanitizer/sanitizer_common/sanitizer_flags.inc | 27 +- libsanitizer/sanitizer_common/sanitizer_libc.h | 4 + libsanitizer/sanitizer_common/sanitizer_printf.cc | 6 + 9 files changed, 436 insertions(+), 22 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/asan/auto_memory_profile.c create mode 100644 gcc/testsuite/c-c++-common/asan/auto_memory_profile_decrease.c create mode 100644 gcc/testsuite/c-c++-common/asan/auto_memory_profile_timestamp.c diff --git a/gcc/testsuite/c-c++-common/asan/auto_memory_profile.c b/gcc/testsuite/c-c++-common/asan/auto_memory_profile.c new file mode 100644 index 0000000..e71a98b --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/auto_memory_profile.c @@ -0,0 +1,35 @@ +// Based on LLVM Heap Profile test. Ported to C, adopted for GCC +// Tests heap_profile=1. +// Printing memory profiling only works in the configuration where we can +// detect leaks. + +/* { dg-do run } */ +/* { dg-set-target-env-var ASAN_OPTIONS "heap_profile=1" } */ + +#include +#include +#include +#include + +char *sink[1000]; + +int main() { + int i; + for (i = 0; i < 3; i++) { + const size_t kSize = 13000000; + char *x = (char*)malloc(sizeof(char) * kSize); + memset(x, i, kSize); + sink[i] = x; + sleep(1); + } + sleep(1); + for (i = 0; i < 3; i++) { + free(sink[i]); + } + return 0; +} + +/* { dg-output "HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".*13000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".*HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)Live\[^\n\r]*(\n|\r\n|\r)26000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".*HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)Live\[^\n\r]*(\n|\r\n|\r)39000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/asan/auto_memory_profile_decrease.c b/gcc/testsuite/c-c++-common/asan/auto_memory_profile_decrease.c new file mode 100644 index 0000000..cb59c2a --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/auto_memory_profile_decrease.c @@ -0,0 +1,37 @@ +// Tests heap_profile=1 with heap_profile_out_decrease=1. +// Fast memory decrease detection requires higher RSS decrease resolution, so +// heap_profile_out_full_lim=1 is required. +// Printing memory profiling only works in the configuration where we can +// detect leaks. + +/* { dg-do run } */ +/* { dg-set-target-env-var ASAN_OPTIONS "heap_profile=1:heap_profile_out_decrease=1:heap_profile_out_full_lim=1" } */ + +#include +#include +#include +#include + +char *sink[1000]; + +int main() { + int i; + for (i = 0; i < 3; i++) { + const size_t kSize = 13000000; + char *x = (char*)malloc(sizeof(char) * kSize); + memset(x, i, kSize); + sink[i] = x; + //sleep(1); + } + sleep(1); + for (i = 0; i < 3; i++) { + free(sink[i]); + sleep(1); + } + sleep(1); + return 0; +} + +/* { dg-output ".*HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)Live\[^\n\r]*(\n|\r\n|\r)39000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".*HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)Live\[^\n\r]*(\n|\r\n|\r)26000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".*HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)Live\[^\n\r]*(\n|\r\n|\r)13000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/asan/auto_memory_profile_timestamp.c b/gcc/testsuite/c-c++-common/asan/auto_memory_profile_timestamp.c new file mode 100644 index 0000000..31df815 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/auto_memory_profile_timestamp.c @@ -0,0 +1,29 @@ +// Based on LLVM Heap Profile test. Ported to C, adopted for GCC +// Tests heap_profile=1. +// Printing memory profiling only works in the configuration where we can +// detect leaks. + +/* { dg-do run } */ +/* { dg-set-target-env-var ASAN_OPTIONS "heap_profile=1:heap_profile_timestamp=1" } */ + +#include +#include +#include +#include + +char *sink[1000]; + +int main() { + int i; + const size_t kSize = 13000000; + char *x = (char*)malloc(sizeof(char) * kSize); + memset(x, 0, kSize); + sink[0] = x; + sleep(1); + free(x); + + return 0; +} + +/* { dg-output "HEAP PROFILE at RSS \[^\n\r]* time \[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".* byte\[^\n\r]*(\n|\r\n|\r)" } */ diff --git a/libsanitizer/asan/asan_memory_profile.cc b/libsanitizer/asan/asan_memory_profile.cc index 5a25785..8a8ae22 100644 --- a/libsanitizer/asan/asan_memory_profile.cc +++ b/libsanitizer/asan/asan_memory_profile.cc @@ -10,17 +10,31 @@ // This file implements __sanitizer_print_memory_profile. //===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_stacktrace_printer.h" #include "sanitizer_common/sanitizer_stoptheworld.h" +#include "sanitizer_common/sanitizer_placement_new.h" #include "lsan/lsan_common.h" #include "asan/asan_allocator.h" #if CAN_SANITIZE_LEAKS +#include +#include + +#if SANITIZER_LINUX && !SANITIZER_GO +#include +#include +#endif + namespace __asan { +class HeapProfile; // Pointer to the global object +HeapProfile *heap_profile = nullptr; // responsible for profiling + struct AllocationSite { u32 id; uptr total_size; @@ -29,7 +43,26 @@ struct AllocationSite { class HeapProfile { public: - HeapProfile() : allocations_(1024) {} + explicit HeapProfile(const CommonFlags *cf) + : allocations_(1024), hp_flags_(cf), hp_start_ts_(NanoTime()) { + if (internal_strcmp(hp_flags_->heap_profile_log_path, "") == 0) { + // HeapProfiler's report_file initialized to the global one + hp_report_file_ = &report_file; + } + else { + // ... or to the private one + hp_report_file_ = &hp_private_report_file_; + hp_report_file_->SetReportPath(hp_flags_->heap_profile_log_path); + } + } + + void HPPrintf(const char *format, ...) { + va_list args; + va_start(args, format); + HPVPrintf(format, args); + va_end(args); + } + void Insert(u32 id, uptr size) { total_allocated_ += size; total_count_++; @@ -44,20 +77,148 @@ class HeapProfile { allocations_.push_back({id, size, 1}); } + enum class HPProfileType { + NONE, + SHORT, + FULL + }; + + HPProfileType NeededProfileType() { + HPProfileType res = HPProfileType::NONE; + static float lim_percent = + (float)(hp_flags_->heap_profile_out_full_lim) / 100; + uptr cur_rss = GetRSS(); + u64 cur_time = NanoTime(); + + // Full profile initiated if and only if one of the following is true: + // * memory usage (Resident Set Size) has increased from the + // previous time; + // * memory usage has decreased, and we're set to track it; + // * profile timeout has expired, and we're set to track it. + if ((cur_rss > prev_rss_ * (1 + lim_percent)) || + (hp_flags_->heap_profile_out_decrease && + (cur_rss < prev_rss_ * (1 - lim_percent))) || + (hp_flags_->heap_profile_full_out_time && + TimeoutHasExpired(HPProfileType::FULL, cur_time))) { + res = HPProfileType::FULL; + prev_rss_ = cur_rss; + prev_profile_ts_ = cur_time; + prev_short_profile_ts_ = cur_time; + } + // Short profile initiated if and only if profile timeout has expired, + // and we're set to track it. + else if (hp_flags_->heap_profile_short_out_time && + TimeoutHasExpired(HPProfileType::SHORT, cur_time)) { + res = HPProfileType::SHORT; + prev_short_profile_ts_ = cur_time; + } + + return res; + } + + void HPPrintHeader(HPProfileType hp_prof_type) { + switch (hp_prof_type) { + case HPProfileType::NONE: + break; + case HPProfileType::SHORT: + HPPrintf("SHORT "); + case HPProfileType::FULL: + // prev_rss_ already set to current RSS + HPPrintf("HEAP PROFILE at RSS %zd%s", + hp_flags_->heap_profile_rss_mb ? prev_rss_ >> 20 : prev_rss_, + hp_flags_->heap_profile_rss_mb ? "Mb" : "b"); + if (hp_flags_->heap_profile_timestamp) { + // print timestamps since creation of HeapProfile object. + // prev_short_profile_ts_ set to current time in either case + u64 nsecs_since_start = prev_short_profile_ts_ - hp_start_ts_; + u64 ts_sec = nsecs_since_start / (1000 * 1000 * 1000); + u64 ts_usec = (nsecs_since_start % (1000 * 1000 * 1000)) / 1000; + HPPrintf(" time %llu.%06llu", ts_sec, ts_usec); + } + HPPrintf("\n"); + break; + } + } + + void HPPrintFooter(HPProfileType hp_prof_type) { + switch (hp_prof_type) { + case HPProfileType::SHORT: + case HPProfileType::FULL: + HPPrintf("\n\n"); + case HPProfileType::NONE: + break; + } + } + + void HPClearAllocPool() { + total_count_ = 0; + total_allocated_ = 0; + allocations_.clear(); + }; + + + private: + // Basically same as __sanitizer::StackTrace::Print(), + // but with HPPrintf() calls. + void PrintStackTrace(StackTrace stack_trace) { + if (stack_trace.trace == nullptr || stack_trace.size == 0) { + HPPrintf(" \n\n"); + return; + } + + InternalScopedString frame_desc(GetPageSizeCached() * 2); + InternalScopedString dedup_token(GetPageSizeCached()); + int dedup_frames = common_flags()->dedup_token_length; + uptr frame_num = 0; + + for (uptr i = 0; i < stack_trace.size && stack_trace.trace[i]; i++) { + // PCs in stack traces are actually the return addresses, that is, + // addresses of the next instructions after the call. + uptr pc = StackTrace::GetPreviousInstructionPc(stack_trace.trace[i]); + SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc); + CHECK(frames); + + for (SymbolizedStack *cur = frames; cur; cur = cur->next) { + frame_desc.clear(); + RenderFrame(&frame_desc, common_flags()->stack_trace_format, + frame_num++, cur->info, common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); + HPPrintf("%s\n", frame_desc.data()); + + if (dedup_frames-- > 0) { + if (dedup_token.length()) + dedup_token.append("--"); + dedup_token.append(cur->info.function); + } + } + + frames->ClearAll(); + } + + // Always print a trailing empty line after stack trace. + HPPrintf("\n"); + if (dedup_token.length()) + HPPrintf("DEDUP_TOKEN: %s\n", dedup_token.data()); + } + + + public: void Print(uptr top_percent) { InternalSort(&allocations_, allocations_.size(), [](const AllocationSite &a, const AllocationSite &b) { return a.total_size > b.total_size; }); - CHECK(total_allocated_); - uptr total_shown = 0; - Printf("Live Heap Allocations: %zd bytes from %zd allocations; " + HPPrintf("Live Heap Allocations: %zd bytes from %zd allocations; " "showing top %zd%%\n", total_allocated_, total_count_, top_percent); + if (UNLIKELY(!total_allocated_)) { + return; + } + uptr total_shown = 0; for (uptr i = 0; i < allocations_.size(); i++) { auto &a = allocations_[i]; - Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size, - a.total_size * 100 / total_allocated_, a.count); - StackDepotGet(a.id).Print(); + HPPrintf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", + a.total_size, a.total_size * 100 / total_allocated_, a.count); + PrintStackTrace(StackDepotGet(a.id)); total_shown += a.total_size; if (total_shown * 100 / total_allocated_ > top_percent) break; @@ -68,31 +229,145 @@ class HeapProfile { uptr total_allocated_ = 0; uptr total_count_ = 0; InternalMmapVector allocations_; + + const CommonFlags *hp_flags_; + + SpinMutex hp_report_file_mu_; + ReportFile hp_private_report_file_ = {&hp_report_file_mu_, + kStderrFd, "", "", 0}; + ReportFile *hp_report_file_; + + // Keep track of previous profile values + uptr prev_rss_ = 0; + u64 prev_profile_ts_ = 0; + u64 prev_short_profile_ts_ = 0; + u64 hp_start_ts_; + + void HPRawWrite(const char *buffer) { + hp_report_file_->Write(buffer, internal_strlen(buffer)); + } + + // A helper function dumping arbitrary sized buffer to report file. + // Basically same as __sanitizer::SharedPrintfCode. + void HPVPrintf(const char *format, va_list args) { + va_list args2; + va_copy(args2, args); + const int kLen = 16 * 1024; + + // |local_buffer| is small enough not to overflow the stack and/or violate + // the stack limit enforced by TSan (-Wframe-larger-than=512). On the other + // hand, the bigger the buffer is, the more the chance the error report will + // fit into it. + char local_buffer[400]; + + int needed_length; + char *buffer = local_buffer; + int buffer_size = ARRAY_SIZE(local_buffer); + + // First try to print a message using a local buffer, and then fall back to + // mmaped buffer. + for (int use_mmap = 0; use_mmap < 2; use_mmap++) { + if (use_mmap) { + va_end(args); + va_copy(args, args2); + buffer = (char*)MmapOrDie(kLen, "HPVPrintf"); + buffer_size = kLen; + } + + needed_length = 0; + needed_length += internal_vsnprintf(buffer + needed_length, + buffer_size - needed_length, + format, args); + + // Check that data fits into the current buffer. + // If it does not, report and die. + // If it does, just print it. + if (needed_length >= buffer_size) { + if (!use_mmap) continue; + RAW_CHECK_MSG(needed_length < kLen, + "Buffer in Report is too short!\n"); + } + break; + } + + HPRawWrite(buffer); + + // If we had mapped any memory, clean it up. + if (buffer != local_buffer) + UnmapOrDie((void *)buffer, buffer_size); + va_end(args2); + } + + bool TimeoutHasExpired(HPProfileType hp_prof_type, u64 cur_time) { + if (UNLIKELY(hp_prof_type == HPProfileType::NONE)) { + return false; + } + + u64 timeout_msecs; + u64 prev_ts; + u64 threshold_ts; + + switch (hp_prof_type) { + case HPProfileType::FULL: + timeout_msecs = hp_flags_->heap_profile_full_out_time; + prev_ts = prev_profile_ts_; + break; + + case HPProfileType::SHORT: + timeout_msecs = hp_flags_->heap_profile_short_out_time; + prev_ts = prev_short_profile_ts_; + break; + + default: + break; + } + + threshold_ts = prev_ts + timeout_msecs * 1000 * 1000; + return cur_time >= threshold_ts; + } }; static void ChunkCallback(uptr chunk, void *arg) { - HeapProfile *hp = reinterpret_cast(arg); AsanChunkView cv = FindHeapChunkByAllocBeg(chunk); if (!cv.IsAllocated()) return; u32 id = cv.GetAllocStackId(); if (!id) return; - hp->Insert(id, cv.UsedSize()); + heap_profile->Insert(id, cv.UsedSize()); } static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list, void *argument) { - HeapProfile hp; - __lsan::ForEachChunk(ChunkCallback, &hp); - hp.Print(reinterpret_cast(argument)); + heap_profile->HPClearAllocPool(); + // No need to pass anything to callback, so just pass null + __lsan::ForEachChunk(ChunkCallback, nullptr); + heap_profile->Print(reinterpret_cast(argument)); } +// Static buffer to be used in placement new +static unsigned char heap_profile_buf[sizeof(HeapProfile)]; + } // namespace __asan +#endif // CAN_SANITIZE_LEAKS + extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_print_memory_profile(uptr top_percent) { - __sanitizer::StopTheWorld(__asan::MemoryProfileCB, (void*)top_percent); +#if CAN_SANITIZE_LEAKS + // If this function is ever called, then the global + // heap_profile object is guaranteed to be used. + if (UNLIKELY(__asan::heap_profile == nullptr)) { + __asan::heap_profile = new(__asan::heap_profile_buf) + __asan::HeapProfile(__sanitizer::common_flags()); + } + + __asan::HeapProfile::HPProfileType hp_prof_type = + __asan::heap_profile->NeededProfileType(); + __asan::heap_profile->HPPrintHeader(hp_prof_type); + if (hp_prof_type == __asan::HeapProfile::HPProfileType::FULL) { + __sanitizer::StopTheWorld(__asan::MemoryProfileCB, (void*)top_percent); + } + __asan::heap_profile->HPPrintFooter(hp_prof_type); +#endif } } // extern "C" - -#endif // CAN_SANITIZE_LEAKS diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cc b/libsanitizer/sanitizer_common/sanitizer_common.cc index d84cd8a..21be01d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.cc +++ b/libsanitizer/sanitizer_common/sanitizer_common.cc @@ -519,5 +519,9 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_print_memory_profile(int top_percent) { (void)top_percent; } + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void HeapProfilePrintf(const char *format, va_list args) { +} #endif } // extern "C" diff --git a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc index a50ab14..3c42911 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc @@ -76,7 +76,7 @@ void BackgroundThread(void *arg) { uptr prev_reported_rss = 0; uptr prev_reported_stack_depot_size = 0; bool reached_soft_rss_limit = false; - uptr rss_during_last_reported_profile = 0; + while (true) { SleepForMillis(100); uptr current_rss_mb = GetRSS() >> 20; @@ -118,11 +118,10 @@ void BackgroundThread(void *arg) { SoftRssLimitExceededCallback(false); } } - if (heap_profile && - current_rss_mb > rss_during_last_reported_profile * 1.1) { - Printf("\n\nHEAP PROFILE at RSS %zdMb\n", current_rss_mb); - __sanitizer_print_memory_profile(90); - rss_during_last_reported_profile = current_rss_mb; + if (heap_profile) { + __sanitizer_print_memory_profile( + common_flags()->heap_profile_top_percent + ); } } } diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.inc b/libsanitizer/sanitizer_common/sanitizer_flags.inc index 4b651ca..836f872 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.inc +++ b/libsanitizer/sanitizer_common/sanitizer_flags.inc @@ -121,7 +121,32 @@ COMMON_FLAG(uptr, soft_rss_limit_mb, 0, " until the RSS goes below the soft limit." " 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, heap_profile, false, + "Experimental. Enables heap profiler (asan-only).") +COMMON_FLAG(bool, heap_profile_timestamp, false, + "If set, prints timestamps along with heap profiler data.") +COMMON_FLAG(int, heap_profile_out_full_lim, 10, + "Threshold value (in percentages) of RSS growth which" + " triggers printing memory usage by heap profiler.") +COMMON_FLAG(bool, heap_profile_out_decrease, false, + "If set, prints memory profile on memory usage decrease.") +COMMON_FLAG(uptr, heap_profile_full_out_time, 0, + "If non-zero, sets timeout (in milliseconds) for unconditional" + " printing of memory usage data by heap profiler; otherwise" + " there's no timeout.") +COMMON_FLAG(uptr, heap_profile_short_out_time, 0, + "If non-zero, sets timeout (in milliseconds) for unconditional" + " printing of short memory usage data by heap profiler;" + " otherwise there's no timeout. This should be less than" + " heap_profile_full_out_time or no short output will be produced," + " as printing full memory profile has higher precedence.") +COMMON_FLAG(int, heap_profile_top_percent, 90, + "Sets threshold (in percentages) for memory allocations to be" + " shown, from largest to smallest.") +COMMON_FLAG(bool, heap_profile_rss_mb, true, + "If true, prints RSS memory usage in MBs instead of bytes.") +COMMON_FLAG(const char *, heap_profile_log_path, "", + "If set, uses separate file to write heap profile reports.") 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 " diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.h b/libsanitizer/sanitizer_common/sanitizer_libc.h index 98a4a9d..f817ef8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libc.h +++ b/libsanitizer/sanitizer_common/sanitizer_libc.h @@ -15,6 +15,8 @@ #ifndef SANITIZER_LIBC_H #define SANITIZER_LIBC_H +#include + // ----------- ATTENTION ------------- // This header should NOT include any other headers from sanitizer runtime. #include "sanitizer_internal_defs.h" @@ -54,6 +56,8 @@ char *internal_strstr(const char *haystack, const char *needle); // Works only for base=10 and doesn't set errno. s64 internal_simple_strtoll(const char *nptr, char **endptr, int base); int internal_snprintf(char *buffer, uptr length, const char *format, ...); +int internal_vsnprintf(char *buffer, uptr length, + const char *format, va_list args); // Return true if all bytes in [mem, mem+size) are zero. // Optimized for the case when the result is true. diff --git a/libsanitizer/sanitizer_common/sanitizer_printf.cc b/libsanitizer/sanitizer_common/sanitizer_printf.cc index c11113d..b6e6dbf 100644 --- a/libsanitizer/sanitizer_common/sanitizer_printf.cc +++ b/libsanitizer/sanitizer_common/sanitizer_printf.cc @@ -305,6 +305,12 @@ void Report(const char *format, ...) { va_end(args); } +int internal_vsnprintf(char *buff, uptr length, + const char *format, + va_list args) { + return VSNPrintf(buff, length, format, args); +} + // Writes at most "length" symbols to "buffer" (including trailing '\0'). // Returns the number of symbols that should have been written to buffer // (not including trailing '\0'). Thus, the string is truncated -- 2.7.4 From 746c34ff87a022d0d372bda1f798b9027f9eb7bd Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Fri, 30 Nov 2018 18:30:55 +0300 Subject: [PATCH 07/16] libsanitizer: add filtering of sleep intervals for BackgroundThread. Previously sleep interval length of BackgroundThread was determined by a hardcoded value, thus limiting time resolution of dependent tools, such as Heap Profiler. This commit implements simple algorithm which remembers last several sleeps (namely 2), their durations and whether or not they were followed by some interesting events (i.e. delivery of some memory usage statistics). Then it tries to predict an optimal sleep length for the next time, changing it dynamically. Change-Id: I6fc3865cbf4b9e8c5dd6ad4222eac2085a612b51 Signed-off-by: Andrey Drobyshev --- libsanitizer/asan/asan_memory_profile.cc | 16 +++ libsanitizer/sanitizer_common/sanitizer_common.h | 6 + .../sanitizer_common/sanitizer_common_libcdep.cc | 122 ++++++++++++++++++++- libsanitizer/sanitizer_common/sanitizer_flags.inc | 4 + 4 files changed, 147 insertions(+), 1 deletion(-) diff --git a/libsanitizer/asan/asan_memory_profile.cc b/libsanitizer/asan/asan_memory_profile.cc index 8a8ae22..9671fdff 100644 --- a/libsanitizer/asan/asan_memory_profile.cc +++ b/libsanitizer/asan/asan_memory_profile.cc @@ -30,6 +30,11 @@ #include #endif +namespace __sanitizer { + // provided by sanitizer_common_libcdep.cc + extern SleepInterval last_sleep_int; +} + namespace __asan { class HeapProfile; // Pointer to the global object @@ -116,6 +121,16 @@ class HeapProfile { return res; } + void HPUpdateSleepInt(HPProfileType hp_prof_type) { + switch(hp_prof_type) { + case HPProfileType::NONE: + break; + case HPProfileType::SHORT: + case HPProfileType::FULL: + __sanitizer::last_sleep_int.ends_with_event = true; + } + } + void HPPrintHeader(HPProfileType hp_prof_type) { switch (hp_prof_type) { case HPProfileType::NONE: @@ -363,6 +378,7 @@ void __sanitizer_print_memory_profile(uptr top_percent) { __asan::HeapProfile::HPProfileType hp_prof_type = __asan::heap_profile->NeededProfileType(); + __asan::heap_profile->HPUpdateSleepInt(hp_prof_type); __asan::heap_profile->HPPrintHeader(hp_prof_type); if (hp_prof_type == __asan::HeapProfile::HPProfileType::FULL) { __sanitizer::StopTheWorld(__asan::MemoryProfileCB, (void*)top_percent); diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h index 3c1d1a5..fb06aa4 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.h +++ b/libsanitizer/sanitizer_common/sanitizer_common.h @@ -765,6 +765,12 @@ INLINE uptr GetPthreadDestructorIterations() { #endif } +// Used for tweaking sleep duration +struct SleepInterval { + int duration_ms; + bool ends_with_event; +}; + void *internal_start_thread(void(*func)(void*), void *arg); void internal_join_thread(void *th); void MaybeStartBackgroudThread(); diff --git a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc index 3c42911..656552f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc @@ -23,6 +23,9 @@ namespace __sanitizer { +// Used externally +SleepInterval last_sleep_int = {common_flags()->bgthread_min_sleep_ms, true}; + bool ReportFile::SupportsColors() { SpinMutexLock l(mu); ReopenIfNecessary(); @@ -68,6 +71,123 @@ void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) { SoftRssLimitExceededCallback = Callback; } +static int MinNonZeroTimeout(void) +{ + int T = Max(common_flags()->heap_profile_full_out_time, + common_flags()->heap_profile_short_out_time); + int t = Min(common_flags()->heap_profile_full_out_time, + common_flags()->heap_profile_short_out_time); + return t > 0 ? t : T; +} + +static int Mid(uptr a, uptr b) +{ + int m = Min(a, b); + int M = Max(a, b); + return m + (M - m + 1) / 2; +} + +// Calculate new BackgroundThread sleep duration based on previous +// sleep intervals. +// +// This algorithm remembers the last several sleeps (namely 2), their +// durations and whether or not they were followed by some interesting +// events (i.e. delivery of memory usage statistics). Based on that criteria, +// it considers every interval to be either interesting or uninteresting. +// If the last sleeping interval was interesting, then we probably need to +// sleep less, and vice versa. How much less (or more) is determined by +// "reference points": +// +// * if last and last but one intervals have different +// interesting/uninteresting attributes, then change is considered to be +// smooth, and duration of last but one interval will serve as a reference +// point: +// 0 low_ref new duration, ms +// -|------|-------------|-----------|------------|-------------|--------> +// low_thrhold last_but_one last high_thrhold +// (uninteresting) (interesting) +// +// * if 2 last intervals are both interesting (both uninteresting), then +// change is considered to be rapid, and low/high threshold will serve +// as a reference point: +// 0 new high_ref duration, ms +// -|------|-------------|---------------|---------|---------|-----------> +// low_thrhold last_but_one last high_thrhold +// (uninteresting) (uninteresting) +// +// High/low thresholds, in turn, are determined by both +// bgthread_min/max_sleep_ms values and profiler timeouts (if the user wants +// us to gather statistics every Tms, there's no sense in sleeping longer +// than Tms). +// +// The algorithm also tries not to change interval duration too rapidly, +// thus multiplying/dividing it by factor of 2 together with calculating +// Mid() value with high/low reference points and choosing more "smooth" +// option. +static int CalcNewSleepInterval(void) +{ + static SleepInterval last_but_one_sleep_int = + {common_flags()->bgthread_min_sleep_ms, true}; + + static const int wanted_timeout = MinNonZeroTimeout(); + static const int high_threshold = wanted_timeout > 0 ? + Min(common_flags()->bgthread_max_sleep_ms, + wanted_timeout) : + common_flags()->bgthread_max_sleep_ms; + static const int low_threshold = wanted_timeout > 0 ? + Min(common_flags()->bgthread_min_sleep_ms, + wanted_timeout) : + common_flags()->bgthread_min_sleep_ms; + + static int last_eventful_dur = high_threshold; + static int last_eventless_dur = low_threshold; + + int new_sleep_dur; + + if (!last_sleep_int.ends_with_event) { + // Increase sleep interval length + last_eventless_dur = last_sleep_int.duration_ms; + if (last_eventful_dur <= last_sleep_int.duration_ms) { + last_eventful_dur = high_threshold; + } + + int high_ref; + if (!last_but_one_sleep_int.ends_with_event || + last_but_one_sleep_int.duration_ms <= last_sleep_int.duration_ms) { + high_ref = high_threshold; + } else { + high_ref = last_but_one_sleep_int.duration_ms; + } + + new_sleep_dur = Min(Min(last_sleep_int.duration_ms * 2, + Mid(last_sleep_int.duration_ms, high_ref)), + last_eventful_dur); + } else { + // Decrease sleep interval length + last_eventful_dur = last_sleep_int.duration_ms; + if (last_eventless_dur >= last_sleep_int.duration_ms) { + last_eventless_dur = low_threshold; + } + + int low_ref; + if (last_but_one_sleep_int.ends_with_event || + last_but_one_sleep_int.duration_ms >= last_sleep_int.duration_ms) { + low_ref = low_threshold; + } else { + low_ref = last_but_one_sleep_int.duration_ms; + } + + new_sleep_dur = Max(Max(last_sleep_int.duration_ms / 2, + Mid(last_sleep_int.duration_ms, low_ref)), + last_eventless_dur); + } + + last_but_one_sleep_int = last_sleep_int; + last_sleep_int = {new_sleep_dur, false}; + + return new_sleep_dur; +} + #if SANITIZER_LINUX && !SANITIZER_GO void BackgroundThread(void *arg) { uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb; @@ -78,7 +198,7 @@ void BackgroundThread(void *arg) { bool reached_soft_rss_limit = false; while (true) { - SleepForMillis(100); + SleepForMillis(CalcNewSleepInterval()); uptr current_rss_mb = GetRSS() >> 20; if (Verbosity()) { // If RSS has grown 10% since last time, print some information. diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.inc b/libsanitizer/sanitizer_common/sanitizer_flags.inc index 836f872..47f812a 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.inc +++ b/libsanitizer/sanitizer_common/sanitizer_flags.inc @@ -121,6 +121,10 @@ COMMON_FLAG(uptr, soft_rss_limit_mb, 0, " until the RSS goes below the soft limit." " This limit does not affect memory allocations other than" " malloc/new.") +COMMON_FLAG(int, bgthread_min_sleep_ms, 2, + "Lower bound for background thread sleeping interval.") +COMMON_FLAG(int, bgthread_max_sleep_ms, 100, + "Upper bound for background thread sleeping interval.") COMMON_FLAG(bool, heap_profile, false, "Experimental. Enables heap profiler (asan-only).") COMMON_FLAG(bool, heap_profile_timestamp, false, -- 2.7.4 From 3c2d2ca79e6295b54803ff2a5a34afc153e841e8 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Tue, 18 Dec 2018 15:44:59 +0300 Subject: [PATCH 08/16] libsanitizer: fix SVACE warnings. * lsan_thread.cc: return value of a function '__lsan::CurrentThreadContext' is dereferenced without checking. * sanitizer_libc.cc: casting a signed value which has type 'char' to a bigger unsigned integer type 'unsigned int' while initializing a variable. * sanitizer_libignore.cc: constructor may not initialize class members of '__sanitizer::LibIgnore'. * sanitizer_printf.cc: 'minimal_num_length' with type 'u8', is promoted to type 'int' 32b in 'minimal_num_length - pos', then sign-extended to type 'unsigned long' 64b. * sanitizer_symbolizer_posix_libcdep.cc: after having been compared to NULL value, pointer (...)->path is passed as 1st parameter in call to function '__sanitizer::LLVMSymbolizer::LLVMSymbolizer', where it is dereferenced. Change-Id: I9ebcd68362d68be8d738f4b9d5eaad3fae796f6a Signed-off-by: Andrey Drobyshev --- libsanitizer/lsan/lsan_thread.cc | 6 ++-- libsanitizer/sanitizer_common/sanitizer_libc.cc | 8 ++--- .../sanitizer_common/sanitizer_libignore.cc | 5 ++- libsanitizer/sanitizer_common/sanitizer_printf.cc | 3 +- .../sanitizer_symbolizer_posix_libcdep.cc | 41 ++++++++++++---------- 5 files changed, 36 insertions(+), 27 deletions(-) diff --git a/libsanitizer/lsan/lsan_thread.cc b/libsanitizer/lsan/lsan_thread.cc index af5ad47..2805327 100644 --- a/libsanitizer/lsan/lsan_thread.cc +++ b/libsanitizer/lsan/lsan_thread.cc @@ -128,8 +128,10 @@ void ThreadJoin(u32 tid) { } void EnsureMainThreadIDIsCorrect() { - if (GetCurrentThread() == 0) - CurrentThreadContext()->os_id = GetTid(); + if (GetCurrentThread() == 0) { + ThreadContext *tc = CurrentThreadContext(); + if (tc) tc->os_id = GetTid(); + } } ///// Interface to the common LSan module. ///// diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.cc b/libsanitizer/sanitizer_common/sanitizer_libc.cc index 0b20d75..4f60bb6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libc.cc +++ b/libsanitizer/sanitizer_common/sanitizer_libc.cc @@ -120,8 +120,8 @@ char* internal_strndup(const char *s, uptr n) { int internal_strcmp(const char *s1, const char *s2) { while (true) { - unsigned c1 = *s1; - unsigned c2 = *s2; + char c1 = *s1; + char c2 = *s2; if (c1 != c2) return (c1 < c2) ? -1 : 1; if (c1 == 0) break; s1++; @@ -132,8 +132,8 @@ int internal_strcmp(const char *s1, const char *s2) { int internal_strncmp(const char *s1, const char *s2, uptr n) { for (uptr i = 0; i < n; i++) { - unsigned c1 = *s1; - unsigned c2 = *s2; + char c1 = *s1; + char c2 = *s2; if (c1 != c2) return (c1 < c2) ? -1 : 1; if (c1 == 0) break; s1++; diff --git a/libsanitizer/sanitizer_common/sanitizer_libignore.cc b/libsanitizer/sanitizer_common/sanitizer_libignore.cc index 4b8cbed..2163551 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libignore.cc +++ b/libsanitizer/sanitizer_common/sanitizer_libignore.cc @@ -16,7 +16,10 @@ namespace __sanitizer { -LibIgnore::LibIgnore(LinkerInitialized) { +LibIgnore::LibIgnore(LinkerInitialized) : mutex_(), count_(0) { + atomic_store_relaxed(&loaded_count_, 0); + internal_memset(code_ranges_, 0, sizeof(code_ranges_)); + internal_memset(libs_, 0, sizeof(libs_)); } void LibIgnore::AddIgnoredLibrary(const char *name_templ) { diff --git a/libsanitizer/sanitizer_common/sanitizer_printf.cc b/libsanitizer/sanitizer_common/sanitizer_printf.cc index b6e6dbf..edeb80f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_printf.cc +++ b/libsanitizer/sanitizer_common/sanitizer_printf.cc @@ -62,7 +62,8 @@ static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value, if (pos < minimal_num_length) { // Make sure compiler doesn't insert call to memset here. internal_memset(&num_buffer[pos], 0, - sizeof(num_buffer[0]) * (minimal_num_length - pos)); + sizeof(num_buffer[0]) * + ((u64)minimal_num_length - (u64)pos)); pos = minimal_num_length; } RAW_CHECK(pos > 0); diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc index 51487b4..419ffd1 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -410,28 +410,31 @@ void Symbolizer::PlatformPrepareForSandboxing() {} static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) { const char *path = common_flags()->external_symbolizer_path; const char *binary_name = path ? StripModuleName(path) : ""; - if (path && path[0] == '\0') { - VReport(2, "External symbolizer is explicitly disabled.\n"); - return nullptr; - } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) { - VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path); - return new(*allocator) LLVMSymbolizer(path, allocator); - } else if (!internal_strcmp(binary_name, "atos")) { + + if (path) { + if (path[0] == '\0') { + VReport(2, "External symbolizer is explicitly disabled.\n"); + return nullptr; + } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) { + VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path); + return new(*allocator) LLVMSymbolizer(path, allocator); + } else if (!internal_strcmp(binary_name, "atos")) { #if SANITIZER_MAC - VReport(2, "Using atos at user-specified path: %s\n", path); - return new(*allocator) AtosSymbolizer(path, allocator); + VReport(2, "Using atos at user-specified path: %s\n", path); + return new(*allocator) AtosSymbolizer(path, allocator); #else // SANITIZER_MAC - Report("ERROR: Using `atos` is only supported on Darwin.\n"); - Die(); + Report("ERROR: Using `atos` is only supported on Darwin.\n"); + Die(); #endif // SANITIZER_MAC - } else if (!internal_strcmp(binary_name, "addr2line")) { - VReport(2, "Using addr2line at user-specified path: %s\n", path); - return new(*allocator) Addr2LinePool(path, allocator); - } else if (path) { - Report("ERROR: External symbolizer path is set to '%s' which isn't " - "a known symbolizer. Please set the path to the llvm-symbolizer " - "binary or other known tool.\n", path); - Die(); + } else if (!internal_strcmp(binary_name, "addr2line")) { + VReport(2, "Using addr2line at user-specified path: %s\n", path); + return new(*allocator) Addr2LinePool(path, allocator); + } else { + Report("ERROR: External symbolizer path is set to '%s' which isn't " + "a known symbolizer. Please set the path to the llvm-symbolizer " + "binary or other known tool.\n", path); + Die(); + } } // Otherwise symbolizer program is unknown, let's search $PATH -- 2.7.4 From 3205e43c48fae043b46f323c30d4ba1372e31e2a Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov Date: Thu, 27 Dec 2018 12:42:37 +0300 Subject: [PATCH 09/16] [asan] Add __libc_siglongjmp/__libc_longjmp interceptors Change-Id: Ic247cb33f17743fa597b3a74baf7cf31c075165d --- libsanitizer/asan/asan_interceptors.cc | 20 ++++++++++++++++++++ libsanitizer/asan/asan_interceptors.h | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/libsanitizer/asan/asan_interceptors.cc b/libsanitizer/asan/asan_interceptors.cc index 1678372..2e99a7f 100644 --- a/libsanitizer/asan/asan_interceptors.cc +++ b/libsanitizer/asan/asan_interceptors.cc @@ -386,6 +386,20 @@ INTERCEPTOR(void, siglongjmp, void *env, int val) { } #endif +#if ASAN_INTERCEPT__LIBC_SIGLONGJMP +INTERCEPTOR(void, __libc_siglongjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(__libc_siglongjmp)(env, val); +} +#endif + +#if ASAN_INTERCEPT__LIBC_LONGJMP +INTERCEPTOR(void, __libc_longjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(__libc_longjmp)(env, val); +} +#endif + #if ASAN_INTERCEPT___CXA_THROW INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { CHECK(REAL(__cxa_throw)); @@ -870,6 +884,12 @@ void InitializeAsanInterceptors() { #if ASAN_INTERCEPT_SIGLONGJMP ASAN_INTERCEPT_FUNC(siglongjmp); #endif +#if ASAN_INTERCEPT__LIBC_SIGLONGJMP + ASAN_INTERCEPT_FUNC(__libc_siglongjmp); +#endif +#if ASAN_INTERCEPT__LIBC_LONGJMP + ASAN_INTERCEPT_FUNC(__libc_longjmp); +#endif // Intercept exception handling functions. #if ASAN_INTERCEPT___CXA_THROW diff --git a/libsanitizer/asan/asan_interceptors.h b/libsanitizer/asan/asan_interceptors.h index 706729c..1d9f30b 100644 --- a/libsanitizer/asan/asan_interceptors.h +++ b/libsanitizer/asan/asan_interceptors.h @@ -40,8 +40,12 @@ #if SANITIZER_LINUX && !SANITIZER_ANDROID # define ASAN_INTERCEPT_SWAPCONTEXT 1 +# define ASAN_INTERCEPT__LIBC_SIGLONGJMP 1 +# define ASAN_INTERCEPT__LIBC_LONGJMP 1 #else # define ASAN_INTERCEPT_SWAPCONTEXT 0 +# define ASAN_INTERCEPT__LIBC_SIGLONGJMP 0 +# define ASAN_INTERCEPT__LIBC_LONGJMP 0 #endif #if !SANITIZER_WINDOWS -- 2.7.4 From 805c262fd7597188635a66f6ed85c7edd31dca48 Mon Sep 17 00:00:00 2001 From: marxin Date: Thu, 27 Dec 2018 09:47:20 +0000 Subject: [PATCH 10/16] Cherry pick libsanitizer patch (https://reviews.llvm.org/D54856). 2018-12-27 Martin Liska PR sanitizer/86229 * asan/asan_errors.cc (ErrorAllocTypeMismatch::Print): Cherry pick rL350085. * asan/asan_errors.h (struct ErrorAllocTypeMismatch): Likewise. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@267432 138bc75d-0d04-0410-961f-82ee72b054a4 (cherry picked from commit 27e7ab5c4e8aa2c89a5e9b91d1b4eb60a435b14f) Change-Id: I016920774a6215f7a638cab88f268baeff049601 --- libsanitizer/ChangeLog | 7 +++++++ libsanitizer/asan/asan_errors.cc | 2 +- libsanitizer/asan/asan_errors.h | 6 +++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog index 3578de6..3057e75 100644 --- a/libsanitizer/ChangeLog +++ b/libsanitizer/ChangeLog @@ -1,3 +1,10 @@ +2018-12-27 Martin Liska + + PR sanitizer/86229 + * asan/asan_errors.cc (ErrorAllocTypeMismatch::Print): Cherry + pick rL350085. + * asan/asan_errors.h (struct ErrorAllocTypeMismatch): Likewise. + 2018-06-13 Denis Khalikov PR sanitizer/86090 diff --git a/libsanitizer/asan/asan_errors.cc b/libsanitizer/asan/asan_errors.cc index 73c4cca..ec05bd7 100644 --- a/libsanitizer/asan/asan_errors.cc +++ b/libsanitizer/asan/asan_errors.cc @@ -151,7 +151,7 @@ void ErrorAllocTypeMismatch::Print() { Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n", alloc_names[alloc_type], dealloc_names[dealloc_type], - addr_description.addr); + addr_description.Address()); Printf("%s", d.EndWarning()); CHECK_GT(dealloc_stack->size, 0); scariness.Print(); diff --git a/libsanitizer/asan/asan_errors.h b/libsanitizer/asan/asan_errors.h index 6262dcf..1ffd994 100644 --- a/libsanitizer/asan/asan_errors.h +++ b/libsanitizer/asan/asan_errors.h @@ -140,8 +140,8 @@ struct ErrorFreeNotMalloced : ErrorBase { struct ErrorAllocTypeMismatch : ErrorBase { // ErrorAllocTypeMismatch doesn't own the stack trace. const BufferedStackTrace *dealloc_stack; - HeapAddressDescription addr_description; AllocType alloc_type, dealloc_type; + AddressDescription addr_description; // VS2013 doesn't implement unrestricted unions, so we need a trivial default // constructor ErrorAllocTypeMismatch() = default; @@ -150,8 +150,8 @@ struct ErrorAllocTypeMismatch : ErrorBase { : ErrorBase(tid), dealloc_stack(stack), alloc_type(alloc_type_), - dealloc_type(dealloc_type_) { - GetHeapAddressInformation(addr, 1, &addr_description); + dealloc_type(dealloc_type_), + addr_description(addr, 1, false) { scariness.Clear(); scariness.Scare(10, "alloc-dealloc-mismatch"); }; -- 2.7.4 From 3ba95a5dff386c92dd0399d5a5a6103e19d65cd3 Mon Sep 17 00:00:00 2001 From: Dongkyun Son Date: Wed, 8 Nov 2017 10:49:50 +0900 Subject: [PATCH 11/16] packaging: add release information to pkgversion string. gcc (Tizen/Linaro GCC %{version} %{gcc_release} %{release}) BASEVER DATESTAMP %{release} can provide more information(revision, build count). Change-Id: I73e4da88ebcee0269c6875b6ef40d5690dc4de67 Signed-off-by: Dongkyun Son --- packaging/gcc-aarch64.spec | 2 +- packaging/gcc-armv7hl.spec | 2 +- packaging/gcc-armv7l.spec | 2 +- packaging/linaro-gcc.spec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packaging/gcc-aarch64.spec b/packaging/gcc-aarch64.spec index 5814a71..52a7c9d 100644 --- a/packaging/gcc-aarch64.spec +++ b/packaging/gcc-aarch64.spec @@ -817,7 +817,7 @@ TCFLAGS="$RPM_OPT_FLAGS" GCJFLAGS="$RPM_OPT_FLAGS" \ --disable-libitm \ } \ --with-bugurl="http://bugs.tizen.org/" \ - --with-pkgversion="Tizen/Linaro GCC %{version} %{gcc_release}" \ + --with-pkgversion="Tizen/Linaro GCC %{version} %{gcc_release} %{release}" \ --target=%{target_arch} \ --host=%{host_arch} \ --build=%{host_arch} diff --git a/packaging/gcc-armv7hl.spec b/packaging/gcc-armv7hl.spec index 0b23f93..3540bfe 100644 --- a/packaging/gcc-armv7hl.spec +++ b/packaging/gcc-armv7hl.spec @@ -817,7 +817,7 @@ TCFLAGS="$RPM_OPT_FLAGS" GCJFLAGS="$RPM_OPT_FLAGS" \ --disable-libitm \ } \ --with-bugurl="http://bugs.tizen.org/" \ - --with-pkgversion="Tizen/Linaro GCC %{version} %{gcc_release}" \ + --with-pkgversion="Tizen/Linaro GCC %{version} %{gcc_release} %{release}" \ --target=%{target_arch} \ --host=%{host_arch} \ --build=%{host_arch} diff --git a/packaging/gcc-armv7l.spec b/packaging/gcc-armv7l.spec index 8f7e676..f9a6241 100644 --- a/packaging/gcc-armv7l.spec +++ b/packaging/gcc-armv7l.spec @@ -817,7 +817,7 @@ TCFLAGS="$RPM_OPT_FLAGS" GCJFLAGS="$RPM_OPT_FLAGS" \ --disable-libitm \ } \ --with-bugurl="http://bugs.tizen.org/" \ - --with-pkgversion="Tizen/Linaro GCC %{version} %{gcc_release}" \ + --with-pkgversion="Tizen/Linaro GCC %{version} %{gcc_release} %{release}" \ --target=%{target_arch} \ --host=%{host_arch} \ --build=%{host_arch} diff --git a/packaging/linaro-gcc.spec b/packaging/linaro-gcc.spec index 2401252..52a1b5f 100644 --- a/packaging/linaro-gcc.spec +++ b/packaging/linaro-gcc.spec @@ -814,7 +814,7 @@ TCFLAGS="$RPM_OPT_FLAGS" GCJFLAGS="$RPM_OPT_FLAGS" \ --disable-libitm \ } \ --with-bugurl="http://bugs.tizen.org/" \ - --with-pkgversion="Tizen/Linaro GCC %{version} %{gcc_release}" \ + --with-pkgversion="Tizen/Linaro GCC %{version} %{gcc_release} %{release}" \ --target=%{target_arch} \ --host=%{host_arch} \ --build=%{host_arch} -- 2.7.4 From 04ccd05397979ff6fe3c2b25365d91aec2970e06 Mon Sep 17 00:00:00 2001 From: jakub Date: Sun, 31 Dec 2017 23:50:32 +0000 Subject: [PATCH 12/16] PR middle-end/83623 * expmed.c (expand_shift_1): For 2-byte rotates by BITS_PER_UNIT, check for bswap in mode rather than HImode and use that in expand_unop too. * gcc.dg/pr83623.c: New test. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@256051 138bc75d-0d04-0410-961f-82ee72b054a4 (cherry picked from commit 2876a3f3bbe119fb2fd0c1f414fa5048367b2ff8) Change-Id: I9adfbbc9525351281742494c6872bca70da2e373 --- gcc/ChangeLog | 7 +++++++ gcc/expmed.c | 5 ++--- gcc/testsuite/ChangeLog | 5 +++++ gcc/testsuite/gcc.dg/pr83623.c | 12 ++++++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/pr83623.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6149d35..10d69ee 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2018-01-01 Jakub Jelinek + + PR middle-end/83623 + * expmed.c (expand_shift_1): For 2-byte rotates by BITS_PER_UNIT, + check for bswap in mode rather than HImode and use that in expand_unop + too. + 2018-11-06 Richard Earnshaw * config/aarch64/aarch64.md (speculation_tracker): Set the mode for diff --git a/gcc/expmed.c b/gcc/expmed.c index 13fad52..92f77c6 100644 --- a/gcc/expmed.c +++ b/gcc/expmed.c @@ -2361,9 +2361,8 @@ expand_shift_1 (enum tree_code code, machine_mode mode, rtx shifted, && CONST_INT_P (op1) && INTVAL (op1) == BITS_PER_UNIT && GET_MODE_SIZE (scalar_mode) == 2 - && optab_handler (bswap_optab, HImode) != CODE_FOR_nothing) - return expand_unop (HImode, bswap_optab, shifted, NULL_RTX, - unsignedp); + && optab_handler (bswap_optab, mode) != CODE_FOR_nothing) + return expand_unop (mode, bswap_optab, shifted, NULL_RTX, unsignedp); if (op1 == const0_rtx) return shifted; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 9d3e242..e5bdc89 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2018-01-01 Jakub Jelinek + + PR middle-end/83623 + * gcc.dg/pr83623.c: New test. + 2018-07-31 Richard Earnshaw * c-c++-common/spec-barrier-1.c: New test. diff --git a/gcc/testsuite/gcc.dg/pr83623.c b/gcc/testsuite/gcc.dg/pr83623.c new file mode 100644 index 0000000..5d62c04 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr83623.c @@ -0,0 +1,12 @@ +/* PR middle-end/83623 */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ +/* { dg-additional-options "-mmovbe" { target i?86-*-* x86_64-*-* } } */ + +unsigned short __attribute__ ((__vector_size__ (16))) x; + +void +foo (void) +{ + x = x << 8 | x >> 8; +} -- 2.7.4 From 8ec426d7ca264f19e419649820ad1c27012e46d7 Mon Sep 17 00:00:00 2001 From: Dongkyun Son Date: Wed, 5 Jun 2019 15:56:45 +0900 Subject: [PATCH 13/16] * gcc.dg/pr83623.c: Add additional options on hppa*-&-hpux* and arm*-*-*. (includes 5839d47d0d65a35fa4791220ef57f50912ca40d5) Change-Id: Icd2eab553e9fd30d4317301a93b2d5e2c56a27e4 Signed-off-by: Dongkyun Son --- gcc/testsuite/gcc.dg/pr83623.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gcc/testsuite/gcc.dg/pr83623.c b/gcc/testsuite/gcc.dg/pr83623.c index 5d62c04..ee0fe2e 100644 --- a/gcc/testsuite/gcc.dg/pr83623.c +++ b/gcc/testsuite/gcc.dg/pr83623.c @@ -2,6 +2,8 @@ /* { dg-do compile } */ /* { dg-options "-O2" } */ /* { dg-additional-options "-mmovbe" { target i?86-*-* x86_64-*-* } } */ +/* { dg-additional-options "-fno-common" { target hppa*-*-hpux* } } */ +/* { dg-additional-options "-march=armv7-a -mfloat-abi=softfp -mfpu=neon" { target arm*-*-* } } */ unsigned short __attribute__ ((__vector_size__ (16))) x; -- 2.7.4 From df8f25587eae8e26e29a1f9833bd3de5181c05c3 Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov Date: Tue, 9 Jul 2019 22:40:49 +0300 Subject: [PATCH 14/16] Add binary annotations support (based on annobin plugin) Change-Id: Ib3571622cd090992ce42305b7c5985fd322322d4 --- gcc/Makefile.in | 1 + gcc/annobin.c | 2074 +++++++++++++++++++++ gcc/annobin.h | 152 ++ gcc/asan.h | 20 + gcc/common.opt | 19 + gcc/cp/decl.c | 2 + gcc/flag-types.h | 9 + gcc/output.h | 1 + gcc/params.def | 15 + gcc/params.h | 6 + gcc/testsuite/gcc.dg/annobin/annobin.exp | 35 + gcc/testsuite/gcc.dg/annobin/build-attributes-1.c | 4 + gcc/testsuite/gcc.dg/annobin/build-attributes-2.c | 5 + gcc/testsuite/gcc.dg/annobin/build-attributes-3.c | 17 + gcc/testsuite/gcc.dg/annobin/build-attributes-4.c | 14 + gcc/testsuite/gcc.dg/annobin/build-attributes-5.c | 11 + gcc/testsuite/gcc.dg/annobin/note-property-1.c | 6 + gcc/testsuite/gcc.dg/annobin/note-property-2.c | 7 + gcc/toplev.c | 5 + gcc/varasm.c | 74 +- 20 files changed, 2461 insertions(+), 16 deletions(-) create mode 100644 gcc/annobin.c create mode 100644 gcc/annobin.h create mode 100644 gcc/testsuite/gcc.dg/annobin/annobin.exp create mode 100644 gcc/testsuite/gcc.dg/annobin/build-attributes-1.c create mode 100644 gcc/testsuite/gcc.dg/annobin/build-attributes-2.c create mode 100644 gcc/testsuite/gcc.dg/annobin/build-attributes-3.c create mode 100644 gcc/testsuite/gcc.dg/annobin/build-attributes-4.c create mode 100644 gcc/testsuite/gcc.dg/annobin/build-attributes-5.c create mode 100644 gcc/testsuite/gcc.dg/annobin/note-property-1.c create mode 100644 gcc/testsuite/gcc.dg/annobin/note-property-2.c diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 2e8fa8a..600fed1 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1203,6 +1203,7 @@ OBJS = \ ggc-page.o \ alias.o \ alloc-pool.o \ + annobin.o \ auto-inc-dec.o \ auto-profile.o \ bb-reorder.o \ diff --git a/gcc/annobin.c b/gcc/annobin.c new file mode 100644 index 0000000..f3d2a4d --- /dev/null +++ b/gcc/annobin.c @@ -0,0 +1,2074 @@ +/* annobin - a gcc plugin for annotating binary files. + Copyright (c) 2017 - 2019 Red Hat. + Created by Nick Clifton. + + This 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. + + It 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. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "annobin.h" +/* These are necessary so that we can call examine the target's options. */ +#include +#include +#include +#include +#include +/* #include */ +#include +/* Needed to access some of GCC's internal structures. */ +#include "cgraph.h" +#include "target.h" +#include "errors.h" +/* For compiler version. */ +#include "version.h" +/* for GEN_INT */ +#include "rtl.h" +/* for gimple_opt_pass */ +#include "gimple.h" +/* for pass_data */ +#include "tree-pass.h" +/* for lookup_attribute */ +#include "stringpool.h" +#include "attribs.h" +/* for sanitize_flags_p */ +#include "asan.h" +/* for crtl */ +#include "memmodel.h" +#include "emit-rtl.h" +#include "params.h" + +/* Version number. NB: Keep the numeric and string versions in sync + Also, keep in sync with the major_version and minor_version definitions + in annocheck.c. + FIXME: This value should be defined in only one place... */ +/* FIXME: define that gcc-side version is used. */ +static unsigned int annobin_version = 871; +/* static const char * annobin_version_string = "Version 871"; */ + +/* Prefix used to isolate annobin symbols from program symbols. */ +#define ANNOBIN_SYMBOL_PREFIX ".annobin_" + +/* Suffix used to turn a section name into a group name. */ +#define ANNOBIN_GROUP_NAME ".group" + +/* FIXME: elimintate concatenations? */ +/* Section names (and section name prefixes) used by gcc. */ +#define CODE_SECTION ".text" +#define HOT_SUFFIX ".hot" +#define HOT_SECTION CODE_SECTION HOT_SUFFIX +#define COLD_SUFFIX ".unlikely" +#define COLD_SECTION CODE_SECTION COLD_SUFFIX +#define STARTUP_SUFFIX ".startup" +#define STARTUP_SECTION CODE_SECTION STARTUP_SUFFIX +#define EXIT_SUFFIX ".exit" +#define EXIT_SECTION CODE_SECTION EXIT_SUFFIX + +/* True if this plugin is enabled. Disabling is permitted so that build + systems can globally enable the plugin, and then have specific build + targets that disable the plugin because they do not want it. */ +static bool enabled = true; + +/* True if the symbols used to map addresses to file names should be global. + On some architectures these symbols have to be global so that they will + be preserved in object files. But doing so can prevent the build-id + mechanism from working, since the symbols contain build-date information. */ +static bool global_file_name_symbols = false; + +/* True if notes about the stack usage should be included. Doing can be useful + if stack overflow problems need to be diagnosed, but they do increase the size + of the note section quite a lot. */ +static bool annobin_enable_stack_size_notes = false; +static unsigned long annobin_total_static_stack_usage = 0; +static unsigned long annobin_max_stack_size = 0; +/* Various command-line compiler options for dynamic notes. */ +uint32_t annobin_gnu_compiler_flags = 0; +/* Annobin_gnu_compiler_flags_sets need for the checking, if already + setted(same bits) */ +uint32_t annobin_gnu_compiler_flags_sets = 0; +/* True if notes about unsanitized functions should be included. */ +bool annobin_enable_unsanitized_function_notes = true; + +/* If a function's static stack size requirement is greater than STACK_THRESHOLD + then a function specific note will be generated indicating the amount of stack + that it needs. */ +static unsigned long stack_threshold = 10240; + +/* Internal variable, used by target specific parts of the annobin plugin as well + as this generic part. True if the object file being generated is for a 64-bit + target. */ +/* FIXME: Do we really need this? */ +static bool annobin_is_64bit = false; + +/* True if the creation of function specific notes should be reported. */ +static bool annobin_function_verbose = true; + +/* True if annobin should generate gcc errors if gcc command line options are wrong. */ +static bool annobin_active_checks = false; + +#ifdef flag_stack_clash_protection +static int global_stack_clash_option = -1; +#endif +#ifdef flag_cf_protection +static int global_cf_option = -1; +#endif +static bool global_omit_frame_pointer; +static bool annobin_enable_attach = false; +static signed int target_start_sym_bias = 0; +static unsigned int annobin_note_count = 0; +static unsigned int global_GOWall_options = 0; +static int global_stack_prot_option = 0; +static int global_pic_option = 0; +static int global_short_enums = 0; +static int global_fortify_level = -1; +static int global_glibcxx_assertions = -1; +static const char * annobin_extra_prefix = ""; +static char * annobin_current_filename = NULL; +static char * annobin_current_endname = NULL; + + +/* Utility function to generate some output. The first argument is a verbosity level. + If it is zero then the output is always generated, otherwise the output is only + generated if the level is less than or equal to the current verbosity setting. */ + +/* FIXME: Could this be done with dump...? */ +static void +annobin_inform (unsigned level, const char * format, ...) +{ + va_list args; + + if (level > 0 && level > ANNOBIN_VERBOSE) + return; + + fflush (stdout); + + fprintf (stderr, "annobin: "); + + if (main_input_filename) + fprintf (stderr, "%s: ", main_input_filename); + + va_start (args, format); + vfprintf (stderr, format, args); + va_end (args); + + putc ('\n', stderr); +} + +static void +ice (const char * text) +{ + gcc_assert (false && text); +} + +/* Create a symbol name to represent the sources we are annotating. + Since there can be multiple input files, we choose the main output + filename (stripped of any path prefixes). Since filenames can + contain characters that symbol names do not (eg '-') we have to + allocate our own name. */ + +static void +init_annobin_current_filename (void) +{ + /* TODO: check DECL_SOURCE_FILE? */ + char * name; + unsigned i; + + if (annobin_current_filename != NULL + || main_input_filename == NULL) + return; + + name = (char *) lbasename (main_input_filename); + + if (strlen (name) == 0) + { + /* The name can be empty if we are receiving the source code + from a pipe. In this case, we invent our own name. */ + name = (char *) "piped_input"; + } + + if (global_file_name_symbols) + name = strcpy ((char *) xmalloc (strlen (name) + 20), name); + else + name = xstrdup (name); + + /* Convert any non-symbolic characters into underscores. */ + for (i = strlen (name); i--;) + { + char c = name[i]; + + if (! ISALNUM (c) && c != '_' && c != '.' && c != '$') + name[i] = '_'; + else if (i == 0 && ISDIGIT (c)) + name[i] = '_'; + } + + if (global_file_name_symbols) + { + /* A program can have multiple source files with the same name. + Or indeed the same source file can be included multiple times. + Or a library can be built from a sources which include file names + that match application file names. Whatever the reason, we need + to be ensure that we generate unique global symbol names. So we + append the time to the symbol name. This will of course break + the functionality of build-ids. That is why this option is off + by default. */ + struct timeval tv; + + if (gettimeofday (& tv, NULL)) + { + ice ("unable to get time of day."); + tv.tv_sec = tv.tv_usec = 0; + } + sprintf (name + strlen (name), + "_%8.8lx_%8.8lx", (long) tv.tv_sec, (long) tv.tv_usec); + } + + annobin_current_filename = concat (ANNOBIN_SYMBOL_PREFIX, annobin_extra_prefix, name, NULL); + annobin_current_endname = concat (annobin_current_filename, "_end", NULL); +} + +static void +annobin_emit_asm (const char * text, const char * comment) +{ + unsigned len = 0; + + if (text) + { + fprintf (asm_out_file, "\t"); + len = fprintf (asm_out_file, "%s", text); + } + if (flag_verbose_asm && comment) + { + if (len == 0) + ; + if (len < 8) + fprintf (asm_out_file, "\t\t"); + else + fprintf (asm_out_file, "\t"); + + fprintf (asm_out_file, "%s %s", ASM_COMMENT_START, comment); + } + + fprintf (asm_out_file, "\n"); +} + + +/* Create the assembler source necessary to build a single ELF Note structure. */ + +void +static annobin_output_note (const char * name, + unsigned namesz, + bool name_is_string, + const char * name_description, + const char * desc1, + const char * desc2, + unsigned descsz, + bool desc_is_string, + unsigned type, + const char * sec_name) +{ + char buffer1[24]; + char buffer2[128]; + unsigned i; + + if (asm_out_file == NULL) + return; + + if (type == NT_GNU_BUILD_ATTRIBUTE_FUNC) + { + if (desc_is_string) + annobin_inform (2, "Create function specific note for: %s: %s", desc1, name_description); + } + + if (strchr (sec_name, ',')) + fprintf (asm_out_file, "\t.pushsection %s\n", sec_name); + else + fprintf (asm_out_file, "\t.pushsection %s, \"\", %%note\n", sec_name); + + /* Note we use 4-byte alignment even on 64-bit targets. This might seem + wrong for 64-bit systems, but the ELF standard does not specify any + alignment requirements for notes, and it matches already established + practice for other types of notes. Plus it helps reduce the size of + the notes on 64-bit systems which is a good thing. */ + fprintf (asm_out_file, "\t.balign 4\n"); + + if (name == NULL) + { + if (namesz) + ice ("null name with non-zero size"); + + annobin_emit_asm (".dc.l 0", "no name"); + } + else if (name_is_string) + { + if (strlen ((char *) name) != namesz - 1) + ice ("name string does not match name size"); + + sprintf (buffer1, ".dc.l %u", namesz); + sprintf (buffer2 , "namesz [= strlen (%s)]", name); + annobin_emit_asm (buffer1, buffer2); + } + else + { + sprintf (buffer1, ".dc.l %u", namesz); + annobin_emit_asm (buffer1, "size of name"); + } + + if (desc1 == NULL) + { + if (descsz) + ice ("null desc1 with non-zero size"); + if (desc2 != NULL) + ice ("non-null desc2 with null desc1"); + + annobin_emit_asm (".dc.l 0", "no description"); + } + else if (desc_is_string) + { + switch (descsz) + { + case 0: + ice ("zero descsz with string description"); + break; + case 4: + if (annobin_is_64bit || desc2 != NULL) + ice ("descz too small"); + if (desc1 == NULL) + ice ("descz too big"); + break; + case 8: + if (annobin_is_64bit) + { + if (desc2 != NULL) + ice ("descz too small"); + } + else + { + if (desc1 == NULL || desc2 == NULL) + ice ("descz too big"); + } + break; + case 16: + if (! annobin_is_64bit || desc1 == NULL || desc2 == NULL) + ice ("descz too big"); + break; + default: + ice ("description string size does not match address size"); + break; + } + + sprintf (buffer1, ".dc.l %u", descsz); + annobin_emit_asm (buffer1, desc2 == NULL ? "descsz [= sizeof (address)]" : "descsz [= 2 * sizeof (address)]"); + } + else + { + if (desc2 != NULL) + ice ("second description not empty for non-string description"); + + sprintf (buffer1, ".dc.l %u", descsz); + annobin_emit_asm (buffer1, "size of description"); + } + + sprintf (buffer1, ".dc.l %#x", type); + annobin_emit_asm (buffer1, + type == NT_GNU_BUILD_ATTRIBUTE_OPEN ? "OPEN" : + type == NT_GNU_BUILD_ATTRIBUTE_FUNC ? "FUNC" : + type == NT_GNU_PROPERTY_TYPE_0 ? "PROPERTY_TYPE_0" : "*UNKNOWN*"); + + if (name) + { + if (name_is_string) + { + fprintf (asm_out_file, "\t.asciz \"%s\"", (char *) name); + } + else + { + fprintf (asm_out_file, "\t.dc.b"); + for (i = 0; i < namesz; i++) + fprintf (asm_out_file, " %#x%c", + ((unsigned char *) name)[i], + i < (namesz - 1) ? ',' : ' '); + } + + annobin_emit_asm (NULL, name_description); + + if (namesz % 4) + { + fprintf (asm_out_file, "\t.dc.b"); + while (namesz % 4) + { + namesz++; + fprintf (asm_out_file, " 0%c", namesz % 4 ? ',' : ' '); + } + annobin_emit_asm (NULL, "padding"); + } + } + + if (desc1) + { + if (desc_is_string) + { + if (annobin_is_64bit) + fprintf (asm_out_file, "\t.quad %s", (char *) desc1); + else + fprintf (asm_out_file, "\t.dc.l %s", (char *) desc1); + + if (target_start_sym_bias) + { + /* We know that the annobin_current_filename symbol has been + biased in order to avoid conflicting with the function + name symbol for the first function in the file. So reverse + that bias here. */ + if (desc1 == annobin_current_filename) + fprintf (asm_out_file, "- %d", target_start_sym_bias); + } + + annobin_emit_asm (NULL, desc2 ? "description [symbol names]" : "description [symbol name]"); + + if (desc2) + { + if (annobin_is_64bit) + fprintf (asm_out_file, "\t.quad %s\n", (char *) desc2); + else + fprintf (asm_out_file, "\t.dc.l %s\n", (char *) desc2); + } + } + else + { + fprintf (asm_out_file, "\t.dc.b"); + + for (i = 0; i < descsz; i++) + { + fprintf (asm_out_file, " %#x", ((unsigned char *) desc1)[i]); + + if (i == (descsz - 1)) + annobin_emit_asm (NULL, "description"); + else if ((i % 8) == 7) + { + annobin_emit_asm (NULL, "description"); + fprintf (asm_out_file, "\t.dc.b"); + } + else + fprintf (asm_out_file, ","); + } + + /* These notes use 4 byte alignment, even on 64-bit systems. */ + if (descsz % 4) + { + fprintf (asm_out_file, "\t.dc.b"); + while (descsz % 4) + { + descsz++; + fprintf (asm_out_file, " 0%c", descsz % 4 ? ',' : ' '); + } + annobin_emit_asm (NULL, "padding"); + } + } + } + + fprintf (asm_out_file, "\t.popsection\n\n"); + fflush (asm_out_file); + + ++ annobin_note_count; +} + +/* Fills in the DESC1, DESC2 and DESCSZ parameters for a call to annobin_output_note. */ +#define DESC_PARAMETERS(DESC1, DESC2) \ + DESC1, DESC2, (DESC1) == NULL ? 0 : (DESC2 == NULL) ? (annobin_is_64bit ? 8 : 4) : (annobin_is_64bit ? 16 : 8) + +static void +annobin_output_static_note (const char * buffer, + unsigned buffer_len, + bool name_is_string, + const char * name_description, + const char * start, + const char * end, + unsigned note_type, + const char * sec_name) +{ + annobin_output_note (buffer, buffer_len, name_is_string, name_description, + DESC_PARAMETERS (start, end), true, note_type, sec_name); +} + +static void +annobin_output_bool_note (const char bool_type, + const bool bool_value, + const char * name_description, + const char * start, + const char * end, + unsigned note_type, + const char * sec_name) +{ + char buffer [6]; + + sprintf (buffer, "GA%c%c", + bool_value ? GNU_BUILD_ATTRIBUTE_TYPE_BOOL_TRUE + : GNU_BUILD_ATTRIBUTE_TYPE_BOOL_FALSE, + bool_type); + + /* Include the NUL byte at the end of the name "string". + This is required by the ELF spec. */ + annobin_output_static_note (buffer, strlen (buffer) + 1, false, name_description, + start, end, note_type, sec_name); +} + +static void +annobin_output_string_note (const char string_type, + const char * string, + const char * name_description, + const char * start, + const char * end, + unsigned note_type, + const char * sec_name) +{ + unsigned int len = strlen (string); + char * buffer; + + buffer = (char *) xmalloc (len + 5); + + sprintf (buffer, "GA%c%c%s", GNU_BUILD_ATTRIBUTE_TYPE_STRING, string_type, string); + + /* Be kind to readers of the assembler source, and do + not put control characters into ascii strings. */ + annobin_output_static_note (buffer, len + 5, ISPRINT (string_type), name_description, + start, end, note_type, sec_name); + + free (buffer); +} + +static void +annobin_output_numeric_note (const char numeric_type, + unsigned long value, + const char * name_description, + const char * start, + const char * end, + unsigned note_type, + const char * sec_name) +{ + unsigned i; + char buffer [32]; + + snprintf (buffer, 32, "GA%c%c", GNU_BUILD_ATTRIBUTE_TYPE_NUMERIC, numeric_type); + + if (value == 0) + { + /* We need to record *two* zero bytes for a zero value. One for + the value itself and one as a NUL terminator, since this is a + name field... */ + buffer [4] = buffer [5] = 0; + i = 5; + } + else + { + for (i = 4; i < sizeof buffer; i++) + { + buffer[i] = value & 0xff; + /* Note - The name field in ELF Notes must be NUL terminated, even if, + like here, it is not really being used as a name. Hence the test + for value being zero is performed here, rather than after the shift. */ + if (value == 0) + break; + value >>= 8; + } + } + + /* If the value needs more than 8 bytes, consumers are unlikely to be able + to handle it. */ + if (i > 12) + ice ("Numeric value too big to fit into 8 bytes"); + if (value) + ice ("Unable to record numeric value"); + + annobin_output_static_note (buffer, i + 1, false, name_description, + start, end, note_type, sec_name); +} + +static int +compute_pic_option (void) +{ + if (flag_pie > 1) + return 4; + if (flag_pie) + return 3; + if (flag_pic > 1) + return 2; + if (flag_pic) + return 1; + return 0; +} + +/* Compute a numeric value representing the settings/levels of + the -O and -g options, and whether -Wall has been used. This + is to help verify the recommended hardening options for binaries. + The format of the number is as follows: + + bits 0 - 2 : debug type (from enum debug_info_type) + bit 3 : with GNU extensions + bits 4 - 5 : debug level (from enum debug_info_levels) + bits 6 - 8 : DWARF version level + bits 9 - 10 : optimization level + bit 11 : -Os + bit 12 : -Ofast + bit 13 : -Og + bit 14 : -Wall. */ + +static unsigned int +compute_GOWall_options (void) +{ + unsigned int val, i; + + /* FIXME: Keep in sync with changes to gcc/flag-types.h:enum debug_info_type. */ + if (write_symbols > VMS_AND_DWARF2_DEBUG) + { + ice ("unknown debug info type"); + val = 0; + } + else + val = write_symbols; + + if (use_gnu_debug_info_extensions) + val |= (1 << 3); + + if (debug_info_level > DINFO_LEVEL_VERBOSE) + ice ("unknown debug info level"); + else + val |= (debug_info_level << 4); + + if (dwarf_version < 2) + { + /* Apparently it is possible for dwarf_version to be -1. Not sure how + this can happen, but handle it anyway. Since DWARF prior to v2 is + deprecated, we use 2 as the version level. */ + val |= (2 << 6); + annobin_inform (1, "dwarf version level %d recorded as 2\n", dwarf_version); + } + else if (dwarf_version > 7) + { + /* FIXME: We only have 3 bits to record the debug level... */ + val |= (7 << 6); + annobin_inform (1, "dwarf version level %d recorded as 7\n", dwarf_version); + } + else + val |= (dwarf_version << 6); + + if (optimize > 3) + val |= (3 << 9); + else + val |= (optimize << 9); + + /* FIXME: It should not be possible to enable more than one of -Os/-Of/-Og, + so the tests below could be simplified. */ + if (optimize_size) + val |= (1 << 11); + if (optimize_fast) + val |= (1 << 12); + if (optimize_debug) + val |= (1 << 13); + + /* Unfortunately -Wall is not recorded by gcc. So we have to scan the + command line... */ + for (i = 0; i < save_decoded_options_count; i++) + { + if (save_decoded_options[i].opt_index == OPT_Wall) + { + val |= (1 << 14); + break; + } + } + + return val; +} + +static void +record_GOW_settings (unsigned int gow, + bool local, + const char * cname, + const char * aname, + const char * aname_end, + const char * sec_name) +{ + char buffer [128]; + unsigned i; + + (void) sprintf (buffer, "GA%cGOW", GNU_BUILD_ATTRIBUTE_TYPE_NUMERIC); + + for (i = 7; i < sizeof buffer; i++) + { + buffer[i] = gow & 0xff; + /* Note - The name field in ELF Notes must be NUL terminated, even if, + like here, it is not really being used as a name. Hence the test + for value being zero is performed here, rather than after the shift. */ + if (gow == 0) + break; + gow >>= 8; + } + + if (local) + { + annobin_inform (1, "Record -g/-O/-Wall status for %s", cname); + annobin_output_note (buffer, i + 1, false, "numeric: -g/-O/-Wall", + DESC_PARAMETERS (aname, aname_end), true, NT_GNU_BUILD_ATTRIBUTE_FUNC, sec_name); + } + else + { + annobin_inform (1, "Record status of -g/-O/-Wall"); + annobin_output_note (buffer, i + 1, false, "numeric: -g/-O/-Wall", + NULL, NULL, 0, false, NT_GNU_BUILD_ATTRIBUTE_OPEN, sec_name); + } +} + +#ifdef flag_stack_clash_protection +static void +record_stack_clash_note (const char * start, const char * end, int type, const char * sec_name) +{ + char buffer [128]; + unsigned len = sprintf(buffer, "GA%cstack_clash", + flag_stack_clash_protection + ? GNU_BUILD_ATTRIBUTE_TYPE_BOOL_TRUE + : GNU_BUILD_ATTRIBUTE_TYPE_BOOL_FALSE); + + annobin_output_static_note (buffer, len + 1, true, "bool: -fstack-clash-protection status", + start, end, type, sec_name); +} +#endif + +#ifdef flag_cf_protection +static void +record_cf_protection_note (const char * start, const char * end, int type, const char * sec_name) +{ + char buffer [128]; + unsigned len = sprintf (buffer, "GA%ccf_protection", GNU_BUILD_ATTRIBUTE_TYPE_NUMERIC); + + /* We bias the flag_cf_protection enum value by 1 so that we do not get confused by a zero value. */ + buffer[++len] = flag_cf_protection + 1; + buffer[++len] = 0; + + annobin_inform (1, "Record cf-protection status of %d", flag_cf_protection); + annobin_output_static_note (buffer, len + 1, false, "numeric: -fcf-protection status", + start, end, type, sec_name); +} +#endif + +static void +record_frame_pointer_note (const char * start, const char * end, int type, const char * sec_name) +{ + char buffer [128]; + unsigned len; + + if (flag_omit_frame_pointer) + len = sprintf (buffer, "GA%comit_frame_pointer", GNU_BUILD_ATTRIBUTE_TYPE_BOOL_TRUE); + else + len = sprintf (buffer, "GA%comit_frame_pointer", GNU_BUILD_ATTRIBUTE_TYPE_BOOL_FALSE); + + annobin_inform (1, "Record omit-frame-pointer status of %d", flag_omit_frame_pointer); + annobin_output_static_note (buffer, len + 1, true, "bool: -fomit-frame-pointer status", + start, end, type, sec_name); +} + + +static const char * +function_asm_name (void) +{ + if (! current_function_decl) + return NULL; + + tree name = DECL_ASSEMBLER_NAME (current_function_decl); + + if (name == NULL) + return NULL; + + const char * id = IDENTIFIER_POINTER (name); + + if (id == NULL) + return NULL; + + /* Functions annotated with the asm() function attribute will have + an asterisk prefix. Skip it, so that we do not generate invalid + assembler symbol names. */ + if (*id == '*') + id ++; + + if (*id == '0') + return NULL; + + return id; +} + +static void +record_fortify_level (int level, int type, const char * sec) +{ + char buffer [128]; + unsigned len = sprintf (buffer, "GA%cFORTIFY", GNU_BUILD_ATTRIBUTE_TYPE_NUMERIC); + + buffer[++len] = level; + buffer[++len] = 0; + annobin_output_note (buffer, len + 1, false, "FORTIFY SOURCE level", + NULL, NULL, 0, false, type, sec); + annobin_inform (1, "Record a FORTIFY SOURCE level of %d", level); +} + +static void +record_glibcxx_assertions (bool on, int type, const char * sec) +{ + char buffer [128]; + unsigned len = sprintf (buffer, "GA%cGLIBCXX_ASSERTIONS", + on ? GNU_BUILD_ATTRIBUTE_TYPE_BOOL_TRUE + : GNU_BUILD_ATTRIBUTE_TYPE_BOOL_FALSE); + + annobin_output_note (buffer, len + 1, false, on ? "_GLIBCXX_ASSERTIONS defined" : "_GLIBCXX_ASSERTIONS not defined", + NULL, NULL, 0, false, type, sec); + annobin_inform (1, "Record a _GLIBCXX_ASSERTIONS as %s", on ? "defined" : "not defined"); +} + +/* This structure provides various names associated with the current + function. The fields are computed in annobin_create_function_notes + and consumed in various places. */ +typedef struct annobin_current_function +{ + const char * func_name; + const char * asm_name; + const char * section_name; + const char * group_name; + bool comdat; + const char * attribute_section_string; + const char * start_sym; + const char * end_sym; + const char * unlikely_section_name; + const char * unlikely_end_sym; +} annobin_current_function; + +static annobin_current_function current_func; + +static void +clear_current_func (void) +{ + free ((void *) current_func.func_name); + free ((void *) current_func.asm_name); + free ((void *) current_func.section_name); + free ((void *) current_func.group_name); + free ((void *) current_func.attribute_section_string); + free ((void *) current_func.start_sym); + free ((void *) current_func.end_sym); + free ((void *) current_func.unlikely_section_name); + free ((void *) current_func.unlikely_end_sym); + + memset (& current_func, 0, sizeof current_func); +} + +static void +annobin_emit_function_notes (bool force) +{ + const char * start_sym = current_func.start_sym; + const char * end_sym = current_func.end_sym; + const char * sec_name = current_func.attribute_section_string; + const char * func_name = current_func.func_name; + + unsigned int count = annobin_note_count; + + /* annobin_target_specific_function_notes (start_sym, end_sym, sec_name, force); */ + + /* If one or more notes were generated by the target specific function + then we no longer need to include the start/end symbols in any + futher notes that we gebenerate. */ + if (annobin_note_count > count) + start_sym = end_sym = NULL; + + if (flag_stack_protect != -1 + && (force + || global_stack_prot_option != flag_stack_protect)) + { + annobin_inform (1, "Recording stack protection status of %d for %s", + flag_stack_protect, func_name); + + annobin_output_numeric_note (GNU_BUILD_ATTRIBUTE_STACK_PROT, flag_stack_protect, + "numeric: -fstack-protector status", + start_sym, end_sym, NT_GNU_BUILD_ATTRIBUTE_FUNC, sec_name); + + /* We no longer need to include the symbols in the notes we generate. */ + start_sym = end_sym = NULL; + } + +#ifdef flag_stack_clash_protection + if (force + || global_stack_clash_option != flag_stack_clash_protection) + { + annobin_inform (1, "Recording stack clash protection status of %d for %s", + flag_stack_clash_protection, func_name); + + record_stack_clash_note (start_sym, end_sym, NT_GNU_BUILD_ATTRIBUTE_FUNC, sec_name); + start_sym = end_sym = NULL; + } +#endif + +#ifdef flag_cf_protection + if (force + || global_cf_option != flag_cf_protection) + { + annobin_inform (1, "Recording control flow protection status of %d for %s", + flag_cf_protection, func_name); + + record_cf_protection_note (start_sym, end_sym, NT_GNU_BUILD_ATTRIBUTE_FUNC, sec_name); + start_sym = end_sym = NULL; + } +#endif + + if (force || global_omit_frame_pointer != flag_omit_frame_pointer) + { + annobin_inform (1, "Recording omit_frame_pointer status of %d for %s", + flag_omit_frame_pointer, func_name); + + record_frame_pointer_note (start_sym, end_sym, NT_GNU_BUILD_ATTRIBUTE_FUNC, sec_name); + start_sym = end_sym = NULL; + } + + if (force + || global_pic_option != compute_pic_option ()) + { + annobin_inform (1, "Recording PIC status of %s", func_name); + annobin_output_numeric_note (GNU_BUILD_ATTRIBUTE_PIC, compute_pic_option (), + "numeric: pic type", start_sym, end_sym, + NT_GNU_BUILD_ATTRIBUTE_FUNC, sec_name); + start_sym = end_sym = NULL; + } + + if (force + || global_GOWall_options != compute_GOWall_options ()) + { + record_GOW_settings (compute_GOWall_options (), true, func_name, start_sym, end_sym, sec_name); + start_sym = end_sym = NULL; + } + + if (flag_short_enums != -1 + && (force + || global_short_enums != flag_short_enums)) + { + annobin_inform (1, "Recording enum size for %s", func_name); + annobin_output_bool_note (GNU_BUILD_ATTRIBUTE_SHORT_ENUM, flag_short_enums, + flag_short_enums ? "bool: short-enums: on" : "bool: short-enums: off", + start_sym, end_sym, NT_GNU_BUILD_ATTRIBUTE_FUNC, sec_name); + start_sym = end_sym = NULL; + } + + if (annobin_enable_stack_size_notes && flag_stack_usage_info) + { + if ((unsigned long) current_function_static_stack_size > stack_threshold) + { + annobin_inform (1, "Recording stack usage of %lu for %s", + (unsigned long) current_function_static_stack_size, + func_name); + + annobin_output_numeric_note (GNU_BUILD_ATTRIBUTE_STACK_SIZE, + current_function_static_stack_size, + "numeric: stack-size", + start_sym, end_sym, NT_GNU_BUILD_ATTRIBUTE_FUNC, sec_name); + start_sym = end_sym = NULL; + } + + annobin_total_static_stack_usage += current_function_static_stack_size; + + if ((unsigned long) current_function_static_stack_size > annobin_max_stack_size) + annobin_max_stack_size = current_function_static_stack_size; + } + + if (force) + { + record_fortify_level (global_fortify_level, NT_GNU_BUILD_ATTRIBUTE_FUNC, sec_name); + record_glibcxx_assertions (global_glibcxx_assertions, NT_GNU_BUILD_ATTRIBUTE_FUNC, sec_name); + } + + if (annobin_enable_unsanitized_function_notes) + { + /* Check if current_function_decl is unsanitized */ + if (!sanitize_flags_p (SANITIZE_ADDRESS)) + { + annobin_output_bool_note (GNU_BUILD_ATTRIBUTE_NO_SANITIZE, + true, "bool: no_sanitize: true", + start_sym, end_sym, NT_GNU_BUILD_ATTRIBUTE_FUNC, sec_name); + start_sym = end_sym = NULL; + } + } +} + +static const char * +annobin_get_section_name (tree decl) +{ + section *fnsec = function_section (decl); + switch (SECTION_STYLE (fnsec)) + { + case SECTION_NAMED: + return fnsec->named.name; + default: + return NULL; + } +} + +static struct cgraph_node * +annobin_get_node (const_tree decl) +{ + return cgraph_node::get (decl); +} + +static void +annobin_emit_symbol (const char * name) +{ + fprintf (asm_out_file, "\t.type %s, STT_NOTYPE\n", name); + fprintf (asm_out_file, "\t.hidden %s\n", name); + fprintf (asm_out_file, "%s:\n", name); +} + + +static void +annobin_prepare_current_function_description (void) +{ + if (current_func.func_name != NULL) + ice ("new function encountered whilst still processing old function"); + + current_func.func_name = current_function_name (); + current_func.asm_name = function_asm_name (); + + if (current_func.func_name == NULL) + { + current_func.func_name = current_func.asm_name; + + if (current_func.func_name == NULL) + { + /* Can this happen ? */ + ice ("function name not available"); + return; + } + } + + if (current_func.asm_name == NULL) + current_func.asm_name = current_func.func_name; + + /* Copy the names so that they are saved. */ + current_func.func_name = concat (current_func.func_name, NULL); + current_func.asm_name = concat (current_func.asm_name, NULL); + + struct cgraph_node * node = annobin_get_node (current_function_decl); + bool startup, exit, unlikely, likely; + + if (node) + { + startup = node->only_called_at_startup; + exit = node->only_called_at_exit; + unlikely = node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED; + likely = node->frequency == NODE_FREQUENCY_HOT; + } + else + startup = exit = unlikely = likely = false; + + current_func.comdat = DECL_COMDAT_GROUP (current_function_decl) != NULL; + + current_func.section_name = annobin_get_section_name (current_function_decl); + + if (current_func.section_name != NULL) + /* This is just so that we can free it later. */ + current_func.section_name = concat (current_func.section_name, NULL); + + else if (current_func.comdat) + { + targetm.asm_out.unique_section (current_function_decl, 0); + current_func.section_name = concat (annobin_get_section_name (current_function_decl), NULL); + } + + else if (flag_function_sections) + { + /* Special case: at -O2 or higher special functions get a prefix added. */ + if (flag_reorder_functions) + { + if (startup) + current_func.section_name = concat (STARTUP_SECTION, ".", current_func.asm_name, NULL); + else if (exit) + current_func.section_name = concat (EXIT_SECTION, ".", current_func.asm_name, NULL); + else if (unlikely) + current_func.section_name = concat (COLD_SECTION, ".", current_func.asm_name, NULL); + else if (likely) + current_func.section_name = concat (HOT_SECTION, ".", current_func.asm_name, NULL); + else + { + current_func.section_name = concat (CODE_SECTION, ".", current_func.asm_name, NULL); + current_func.unlikely_section_name = concat (COLD_SECTION, ".", current_func.asm_name, NULL); + } + } + else + current_func.section_name = concat (CODE_SECTION, ".", current_func.asm_name, NULL); + } + + else if (flag_reorder_functions /* && targetm_common.have_named_sections */) + { + /* Attempt to determine the section into which the code will be placed. + We could call targetm.asm_out_function_section but that ends up calling + get_section() which will *create* a section if none exists. This causes + problems because later on gcc will attempt to create the section again + but this time it might be using different flags. + + So instead we duplicate the code in gcc/varasm.c:default_function_section() + except that we do not actually call get_named_text_section(). */ + + if (unlikely) + { + /* FIXME: Never actually seen this case occur... */ + current_func.section_name = concat (COLD_SECTION, NULL); + } + else if (startup) + { + if (!in_lto_p && ! flag_profile_values) + current_func.section_name = concat (STARTUP_SECTION, NULL); + } + else if (exit) + { + current_func.section_name = concat (EXIT_SECTION, NULL); + } + else if (likely) + { + /* FIXME: Never seen this one, either. */ + if (!in_lto_p && ! flag_profile_values) + current_func.section_name = concat (HOT_SECTION, NULL); + } + } +} + +/* Create any notes specific to the current function. */ + +static void +annobin_create_function_notes (void) +{ + unsigned int count; + bool force; + + if (! annobin_static_notes_p () || asm_out_file == NULL) + return; + + annobin_prepare_current_function_description (); + + annobin_inform (1, "Function '%s' is assumed to be in section '%s'", + current_func.asm_name, + current_func.section_name ? current_func.section_name : CODE_SECTION); + + /* If the function is going to be in its own section, then we do not know + where it will end up in memory. In particular we cannot rely upon it + being included in the memory range covered by the global notes. So for + such functions we always generate a set of notes. + + FIXME: We do not currently generate a full range of notes. */ + force = current_func.section_name != NULL; + + if (force) + { + if (current_func.comdat) + current_func.group_name = concat (IDENTIFIER_POINTER (DECL_COMDAT_GROUP (current_function_decl)), NULL); + else + current_func.group_name = concat (current_func.section_name, ANNOBIN_GROUP_NAME, NULL); + + /* Include a group name in our attribute section name. */ + current_func.attribute_section_string = concat (GNU_BUILD_ATTRS_SECTION_NAME, current_func.section_name, + ", \"G\", %note, ", + current_func.group_name, + current_func.comdat ? ", comdat" : "", + NULL); + } + else + { + if (current_func.comdat) + ice ("current function is comdat but has no function section"); + + current_func.group_name = NULL; + current_func.attribute_section_string = concat (GNU_BUILD_ATTRS_SECTION_NAME, NULL); + } + + /* We use our own function start and end symbols so that they will + not interfere with the program proper. In particular if we use + the function name symbol ourselves then we can cause problems + when the linker attempts to resolve relocs against it and finds + that it has both PC relative and abolsute relocs. + + We try our best to ensure that the new symbols will not clash + with any other symbols in the program. */ + if (current_function_default_section_suffix ()) + { + current_func.start_sym = concat (ANNOBIN_SYMBOL_PREFIX, current_func.asm_name, current_function_default_section_suffix (), ".start", NULL); + + if (crtl->has_bb_partition) + current_func.end_sym = concat (ANNOBIN_SYMBOL_PREFIX, current_func.asm_name, ".cold", ".end", NULL); + else + current_func.end_sym = concat (ANNOBIN_SYMBOL_PREFIX, current_func.asm_name, current_function_default_section_suffix (), ".end", NULL); + } + else + { + current_func.start_sym = concat (ANNOBIN_SYMBOL_PREFIX, current_func.asm_name, ".start", NULL); + + if (crtl->has_bb_partition) + current_func.end_sym = concat (ANNOBIN_SYMBOL_PREFIX, current_func.asm_name, "cold.end", NULL); + else + current_func.end_sym = concat (ANNOBIN_SYMBOL_PREFIX, current_func.asm_name, ".end", NULL); + } + + count = annobin_note_count; + annobin_emit_function_notes (force); + + if (annobin_note_count > count) + { + /* If we generated any notes then we must make sure that the start + symbol has been emitted as well. The end symbols will be emitted + by annobin_create_function_end_symbol, once the body of the function + has been written to the assembler file. + + Note we cannot just use ".equiv start_sym, asm_name", as the + assembler symbol might have a special type, eg ifunc, and this + would be inherited by our symbol. */ + + /* Switch to the code section. Make sure that we declare the section + in the same way that gcc will declare it. In particular note that + gcc will not add a group notation for non-comdat sections. */ + if (current_func.section_name == NULL) + fprintf (asm_out_file, "\t.pushsection %s\n", CODE_SECTION); + else if (current_func.comdat) + fprintf (asm_out_file, "\t.pushsection %s, \"axG\", %%progbits, %s, comdat\n", + current_func.section_name, current_func.group_name); + else + fprintf (asm_out_file, "\t.pushsection %s, \"ax\", %%progbits\n", current_func.section_name); + + /* Add the start symbol. */ + annobin_emit_symbol (current_func.start_sym); + fprintf (asm_out_file, "\t.popsection\n"); + } + else + { + /* No notes were emitted. We do not need the symbols or anything else. */ + clear_current_func (); + return; + } + + if (current_func.unlikely_section_name) + { + const char * saved_end_sym; + + /* If there is a possibility that GCC might generate an cold section + variant of the current function section, then we need to annotate + that as well. */ + + current_func.start_sym = concat (ANNOBIN_SYMBOL_PREFIX, current_func.asm_name, ".start", COLD_SECTION, NULL); + current_func.unlikely_end_sym = concat (ANNOBIN_SYMBOL_PREFIX, current_func.asm_name, ".end", COLD_SECTION, NULL); + + saved_end_sym = current_func.end_sym; + current_func.end_sym = current_func.unlikely_end_sym; + annobin_emit_function_notes (true); + + /* Add the start symbol. */ + fprintf (asm_out_file, "\t.pushsection %s, \"ax\", %%progbits\n", + current_func.unlikely_section_name); + annobin_emit_symbol (current_func.start_sym); + fprintf (asm_out_file, "\t.popsection\n"); + + current_func.end_sym = saved_end_sym; + } +} + +typedef struct attach_item +{ + const char * section_name; + const char * group_name; + struct attach_item * next; +} attach_item; + +static attach_item * attach_list = NULL; + +static void +queue_attachment (const char * section_name, const char * group_name) +{ + attach_item * item = (attach_item *) xmalloc (sizeof item); + + item->section_name = concat (section_name, NULL); + item->group_name = concat (group_name, NULL); + item->next = attach_list; + attach_list = item; +} + +static void +emit_queued_attachments (void) +{ + if (!annobin_enable_attach) + return; + + attach_item * item; + attach_item * next = NULL; + for (item = attach_list; item != NULL; item = next) + { + const char * name = item->section_name; + + fprintf (asm_out_file, "\t.pushsection %s\n", name); + fprintf (asm_out_file, "\t.attach_to_group %s", item->group_name); + if (flag_verbose_asm) + fprintf (asm_out_file, " %s Add the %s section to the %s group", + ASM_COMMENT_START, name, item->group_name); + fprintf (asm_out_file, "\n"); + fprintf (asm_out_file, "\t.popsection\n"); + + // FIXME: BZ 1684148: These free()s are triggering "attempt to free unallocated + // memory" errors from the address sanitizer. I have no idea why, as they were + // allocated by concat. So for now, just leave them be. The memory will be + // released when gcc terminates. + // free ((void *) item->section_name); + // free ((void *) item->group_name); + next = item->next; + // FIXME: BZ #1638371 reports that this free() triggers an "invalid pointer" + // error when running under MALLOC_CHECK_. I have no idea why, as the + // pointer certainly looks valid to me. So for now, suppress the free. + // free ((void *) item); + } +} + +static void +annobin_create_function_end_symbol () +{ + if (! annobin_static_notes_p () || asm_out_file == NULL) + return; + + if (current_func.end_sym == NULL) + return; + + /* Emit an end symbol for the code in the current function. + First, we have to switch to the correct section. */ + + if (current_func.section_name == NULL) + fprintf (asm_out_file, "\t.pushsection %s\n", CODE_SECTION); + + else if (current_func.comdat) + fprintf (asm_out_file, "\t.pushsection %s, \"axG\", %%progbits, %s, comdat\n", + current_func.section_name, current_func.group_name); + + else + { + if (current_func.unlikely_section_name) + { + /* Emit the end symbol in the unlikely section. + Note - we attempt to create a new section that will be appended to the + end of the sections that are going into the section group. */ + fprintf (asm_out_file, "\t.pushsection %s.zzz, \"ax\", %%progbits\n", + current_func.unlikely_section_name); + annobin_emit_symbol (current_func.unlikely_end_sym); + fprintf (asm_out_file, "\t.popsection\n"); + + /* Make sure that the unlikely section will be added into the + current function's group. */ + if (annobin_enable_attach) + queue_attachment (current_func.unlikely_section_name, + current_func.group_name); + } + + + if (crtl->has_bb_partition) + fprintf (asm_out_file, "\t.pushsection .text.%s.unlikely\n", current_func.asm_name); + else + fprintf (asm_out_file, "\t.pushsection %s\n", current_func.section_name); + + /* We have a problem. We want to create a section group containing + the function section, the note section and the relocations. But + we cannot just emit: + + .section .text.foo, "axG", %%progbits, foo.group + + because GCC will emit its own section definition, which does not + attach to a group: + + .section .text.foo, "ax", %%progbits + + This will create a *second* section called .text.foo, which is + *not* in the group. The notes generated by annobin will be + attached to the group, but the code generated by gcc will not. + + We cannot create a reference from the non-group'ed section + to the group'ed section as this will create a DT_TEXTREL entry + (ie dynamic text relocation) which is not allowed. + + We cannot access GCC's section structure and set the + SECTION_DECLARED flag as the hash tab holding the structures is + private to the varasm.c file. + + We cannot intercept the asm_named_section() function in GCC as + this is defined by the TARGET_ASM_NAMED_SECTION macro, rather + than being defined in the target structure. + + If we omit the section group then the notes will work for + retained sections, but they will not be removed for any garbage + collected code. So then you will have notes covering address + ranges that are probably used for something else. + + The solution for now is to attach GCC's .text.foo section to the + group created for annobin's .text.foo section by using a new + assembler pseudo-op. This can be disabled to allow the plugin + to work with older assemblers, although it does mean that notes + for function sections will be discarded by the linker. + + Note - we do not have to do this for COMDAT sections as they are + already part of a section group, and gcc always includes the group + name in its .section directives. + + Note - we do not emit these attach directives here as function + sections can be reused. So instead we accumulate them and issue + them all at the end of compilation. */ + if (annobin_enable_attach) + queue_attachment (current_func.section_name, current_func.group_name); + } + + annobin_inform (1, "Function '%s' is assumed to end in section '%s'", + current_func.asm_name, + current_func.section_name ? current_func.section_name : CODE_SECTION); + + annobin_emit_symbol (current_func.end_sym); + fprintf (asm_out_file, "\t.popsection\n"); + + clear_current_func (); +} + +static void +annobin_emit_start_sym_and_version_note (const char * suffix, + const char producer_char) +{ + if (* suffix) + { + if (annobin_enable_attach) + /* We put suffixed text sections into a group so that the linker + can delete the notes if the code is discarded. */ + fprintf (asm_out_file, "\t.pushsection %s%s, \"axG\", %%progbits, %s%s%s\n", + CODE_SECTION, suffix, + CODE_SECTION, suffix, ANNOBIN_GROUP_NAME); + else + fprintf (asm_out_file, "\t.pushsection %s%s, \"ax\", %%progbits\n", + CODE_SECTION, suffix); + } + else + fprintf (asm_out_file, "\t.pushsection %s\n", CODE_SECTION); + + fprintf (asm_out_file, "\t%s %s%s\n", global_file_name_symbols ? ".global" : ".hidden", + annobin_current_filename, suffix); + + /* Note - we used to set the type of the symbol to STT_OBJECT, but that is + incorrect because that type is for: + "A data object, such as a variable, an array, and so on". + + There is no ELF symbol to represent a compilation unit, (STT_FILE only + covers a single source file and has special sematic requirements), so + instead we use STT_NOTYPE. (Ideally we could use STT_LOOS+n, but there + is a problem with the GAS assembler, which does not allow such values to + be set on symbols). */ + fprintf (asm_out_file, "\t.type %s%s, STT_NOTYPE\n", annobin_current_filename, suffix); + + if (target_start_sym_bias) + { + /* We set the address of the start symbol to be the current address plus + a bias value. That way this symbol will not be confused for a file + start/function start symbol. + + There is special code in annobin_output_note() that undoes this bias + when the symbol's address is being used to compute a range for the + notes. */ + fprintf (asm_out_file, "\t.set %s%s, . + %d\n", annobin_current_filename, suffix, target_start_sym_bias); + } + else + fprintf (asm_out_file, "\t.equiv %s%s, .\n", annobin_current_filename, suffix); + + /* We explicitly set the size of the symbol to 0 so that it will not + confuse other tools (eg GDB, elfutils) which look for symbols that + cover an address range. */ + fprintf (asm_out_file, "\t.size %s%s, 0\n", annobin_current_filename, suffix); + + fprintf (asm_out_file, "\t.popsection\n"); + + const char * start = concat (annobin_current_filename, suffix, NULL); + const char * end = concat (annobin_current_endname, suffix, NULL); + const char * sec; + + if (* suffix) + sec = concat (GNU_BUILD_ATTRS_SECTION_NAME, suffix, + ", \"G\", %note, " CODE_SECTION, suffix, ANNOBIN_GROUP_NAME, NULL); + else + sec = concat (GNU_BUILD_ATTRS_SECTION_NAME, suffix, NULL); + + char buffer [124]; + + sprintf (buffer, "%d%c%d", SPEC_VERSION, producer_char, annobin_version); + annobin_output_string_note (GNU_BUILD_ATTRIBUTE_VERSION, buffer, + "string: version", start, end, NT_GNU_BUILD_ATTRIBUTE_OPEN, sec); + + free ((void *) sec); + free ((void *) end); + free ((void *) start); +} + +static void +emit_global_notes (const char * suffix) +{ + const char * sec = concat (GNU_BUILD_ATTRS_SECTION_NAME, suffix, NULL); + + /* Record the version of the compiler. */ + annobin_output_string_note (GNU_BUILD_ATTRIBUTE_TOOL, version_string, + "string: build-tool", NULL, NULL, NT_GNU_BUILD_ATTRIBUTE_OPEN, sec); + + /* Record optimization level, -W setting and -g setting */ + record_GOW_settings (global_GOWall_options, false, NULL, NULL, NULL, sec); + + /* Record -fstack-protector option. */ + annobin_output_numeric_note (GNU_BUILD_ATTRIBUTE_STACK_PROT, + /* See BZ 1563141 for an example where global_stack_protection can be -1. */ + global_stack_prot_option >=0 ? global_stack_prot_option : 0, + "numeric: -fstack-protector status", + NULL, NULL, NT_GNU_BUILD_ATTRIBUTE_OPEN, sec); + +#ifdef flag_stack_clash_protection + /* Record -fstack-clash-protection option. */ + record_stack_clash_note (NULL, NULL, NT_GNU_BUILD_ATTRIBUTE_OPEN, sec); +#endif +#ifdef flag_cf_protection + /* Record -fcf-protection option. */ + record_cf_protection_note (NULL, NULL, NT_GNU_BUILD_ATTRIBUTE_OPEN, sec); +#endif + + record_fortify_level (global_fortify_level, NT_GNU_BUILD_ATTRIBUTE_OPEN, sec); + record_glibcxx_assertions (global_glibcxx_assertions, NT_GNU_BUILD_ATTRIBUTE_OPEN, sec); + + /* Record the PIC status. */ + annobin_output_numeric_note (GNU_BUILD_ATTRIBUTE_PIC, global_pic_option, + "numeric: PIC", NULL, NULL, NT_GNU_BUILD_ATTRIBUTE_OPEN, sec); + + /* Record enum size. */ + annobin_output_bool_note (GNU_BUILD_ATTRIBUTE_SHORT_ENUM, global_short_enums != 0, + global_short_enums != 0 ? "bool: short-enums: on" : "bool: short-enums: off", + NULL, NULL, NT_GNU_BUILD_ATTRIBUTE_OPEN, sec); + + record_frame_pointer_note (NULL, NULL, NT_GNU_BUILD_ATTRIBUTE_OPEN, sec); + + /* Record target specific notes. */ + /* annobin_record_global_target_notes (sec); */ + + free ((void *) sec); +} + +void +annobin_record_global_options (void) +{ + int i; + + /* Record global information. + Note - we do this here, rather than in plugin_init() as some + information, PIC status or POINTER_SIZE, may not be initialised + until after the target backend has had a chance to process its + command line options, and this happens *after* plugin_init. */ + + /* Compute the default data size. */ + switch (POINTER_SIZE) + { + case 16: + case 32: + annobin_is_64bit = false; break; + case 64: + annobin_is_64bit = true; break; + default: + annobin_inform (0, "Unknown target pointer size: %d", POINTER_SIZE); + } + + if (annobin_enable_stack_size_notes) + /* We must set this flag in order to obtain per-function stack usage info. */ + flag_stack_usage_info = 1; + +#ifdef flag_stack_clash_protection + global_stack_clash_option = flag_stack_clash_protection; +#endif +#ifdef flag_cf_protection + global_cf_option = flag_cf_protection; +#endif + global_stack_prot_option = flag_stack_protect; + global_pic_option = compute_pic_option (); + global_short_enums = flag_short_enums; + global_GOWall_options = compute_GOWall_options (); + global_omit_frame_pointer = flag_omit_frame_pointer; + + if (annobin_active_checks && optimize < 2 && ! optimize_debug) + error ("optimization level is too low!"); + + /* Look for -D _FORTIFY_SOURCE= and -D_GLIBCXX_ASSERTIONS on the + original gcc command line. Scan backwards so that we record the + last version of the option, should multiple versions be set. */ + +#define FORTIFY_OPTION "_FORTIFY_SOURCE" +#define GLIBCXX_OPTION "_GLIBCXX_ASSERTIONS" + + for (i = save_decoded_options_count; i--;) + { + if (save_decoded_options[i].opt_index == OPT_U) + { + if (save_decoded_options[i].arg == NULL) + continue; + + annobin_inform (2, "decoded arg -U%s", save_decoded_options[i].arg); + + if (strncmp (save_decoded_options[i].arg, FORTIFY_OPTION, strlen (FORTIFY_OPTION)) == 0) + { + if (global_fortify_level == -1) + global_fortify_level = 0; + } + else if (strncmp (save_decoded_options[i].arg, GLIBCXX_OPTION, strlen (GLIBCXX_OPTION)) == 0) + { + if (global_glibcxx_assertions == -1) + global_glibcxx_assertions = false; + } + } + else if (save_decoded_options[i].opt_index == OPT_D) + { + if (save_decoded_options[i].arg == NULL) + continue; + + annobin_inform (2, "decoded arg -D%s", save_decoded_options[i].arg); + + if (strncmp (save_decoded_options[i].arg, FORTIFY_OPTION, strlen (FORTIFY_OPTION)) == 0) + { + int level = atoi (save_decoded_options[i].arg + strlen (FORTIFY_OPTION) + 1); + + if (level < 0 || level > 3) + { + annobin_inform (0, "Unexpected value in -D" FORTIFY_OPTION "%s", + save_decoded_options[i].arg); + level = 0; + } + + if (global_fortify_level == -1) + global_fortify_level = level; + } + + else if (strncmp (save_decoded_options[i].arg, GLIBCXX_OPTION, strlen (GLIBCXX_OPTION)) == 0) + { + if (global_glibcxx_assertions == -1) + global_glibcxx_assertions = true; + } + } + } + + if (global_fortify_level == -1 || global_glibcxx_assertions == -1) + { + /* Not all gcc command line options get passed on to cc1 (or cc1plus). + So if we have not see one of the options that interests us we check + the COLLECT_GCC_OPTIONS environment variable instead. */ + const char * cgo = getenv ("COLLECT_GCC_OPTIONS"); + + if (cgo != NULL) + { + if (global_fortify_level == -1) + { + int level = -1; + const char * fort = cgo; + + while ((fort = strstr (fort, FORTIFY_OPTION)) != NULL) + { + const char * next_fort = fort + strlen (FORTIFY_OPTION); + + if (fort[-1] == 'U') + level = 0; + else + level = atoi (next_fort + 1); + + fort = next_fort; + } + + if (level != -1) + { + if (level < 0 || level > 3) + { + annobin_inform (0, "Unexpected value in -D" FORTIFY_OPTION); + level = 0; + } + + global_fortify_level = level; + } + } + + if (global_glibcxx_assertions == -1) + { + int on = -1; + const char * glca = cgo; + + while ((glca = strstr (glca, GLIBCXX_OPTION)) != NULL) + { + if (glca[-1] == 'U') + on = false; + else + on = true; + + glca = glca + strlen (GLIBCXX_OPTION); + } + + if (on != -1) + global_glibcxx_assertions = on; + } + } + } +} + +static void +annobin_emit_end_symbol (const char * suffix) +{ + fprintf (asm_out_file, "# annobin_emit_end_symbol:suffix=%s\n", suffix); + if (*suffix) + { + fprintf (asm_out_file, "\t.pushsection %s%s\n", CODE_SECTION, suffix); + + /* We want the end symbol to appear at the end of the section. + But if we are creating a symbol for the hot or cold sections + then there can be multiple copies of this section (with the + same name and identical attributes)! So we create a *new* + section just for the end symbol. The linker's normal section + concatenation heuristic should then place this section after + all the others. + + Note however that it we are reversing a symbol bias we cannot + do this, as the arithmetic has to be between symbols defined + in the same section. Fortunately it appears that gcc does not + perform hot/cold partitioning for the PPC64, and this is the + only target that uses symbol biasing. */ + const char * extra_suffix = target_start_sym_bias ? "" : ".zzz"; + + if (annobin_enable_attach) + /* Since we have issued the .attach, make sure that we include the group here. */ + fprintf (asm_out_file, "\t.section %s%s%s, \"axG\", %%progbits, %s%s%s\n", + CODE_SECTION, suffix, extra_suffix, + CODE_SECTION, suffix, ANNOBIN_GROUP_NAME); + else + fprintf (asm_out_file, "\t.section %s%s%s\n", CODE_SECTION, suffix, extra_suffix); + } + else + fprintf (asm_out_file, "\t.pushsection %s\n", CODE_SECTION); + + fprintf (asm_out_file, "\t%s %s%s\n", + global_file_name_symbols ? ".global" : ".hidden", + annobin_current_endname, suffix); + fprintf (asm_out_file, "%s%s:\n", annobin_current_endname, suffix); + fprintf (asm_out_file, "\t.type %s%s, STT_NOTYPE\n", annobin_current_endname, suffix); + fprintf (asm_out_file, "\t.size %s%s, 0\n", annobin_current_endname, suffix); + + /* If there is a bias to the start symbol, we can end up with the case where + the start symbol is after the end symbol. (If the section is empty). + Catch that and adjust the start symbol. This also pacifies eu-elflint + which complains about the start symbol being placed beyond the end of + the section. */ + if (target_start_sym_bias) + { + /* Note: we cannot test "start sym > end sym" as these symbols may not have values + yet, (due to the possibility of linker relaxation). But we are allowed to + test for symbol equality. So we fudge things a little.... */ + + fprintf (asm_out_file, "\t.if %s%s == %s%s + 2\n", annobin_current_filename, suffix, + annobin_current_endname, suffix); + fprintf (asm_out_file, "\t .set %s%s, %s%s\n", annobin_current_filename, suffix, + annobin_current_endname, suffix); + fprintf (asm_out_file, "\t.endif\n"); + } + + fprintf (asm_out_file, "\t.popsection\n"); +} + +/* Emit per-translation unit global gnu.build.attributes. */ +static void +annobin_create_global_build_attributes () +{ + /* It is possible that no code will end up in the .text section. + Eg because the compilation was run with the -ffunction-sections option. + Nevertheless we generate this symbol in the .text section + as at this point we cannot know which section(s) will be used + by compiled code. */ + annobin_emit_start_sym_and_version_note ("", 'p'); + emit_global_notes (""); +} + +void +annobin_target_specific_loader_notes (char * ptr, size_t size) +{ + +} + +void +annobin_search_abi_declaration (tree decl) +{ + if (!annobin_dynamic_notes_p ()) + return; + tree NODE = decl; + const char *decl_str = NULL; + if (TREE_CODE (NODE) == FUNCTION_DECL) { + const char *decl_str = current_function_name (); + if (strstr(decl_str, "std::basic_string") != NULL) + { + annobin_gnu_compiler_flags &= ~GNU_PROPERTY_USECXX11_ABI; + annobin_gnu_compiler_flags_sets |= GNU_PROPERTY_USECXX11_ABI; + } + } + if (TREE_CODE (NODE) == VAR_DECL) { + if (!DECL_ASSEMBLER_NAME_SET_P (NODE)) { + return; + } + + NODE = DECL_ASSEMBLER_NAME (NODE); + if (!NODE) + { + annobin_inform(1, "Can't get assembler name"); + return; + } + + decl_str = IDENTIFIER_POINTER(NODE); + + if (!decl_str) + { + annobin_inform(1, "Can't get the variable identifier"); + return; + } + + /* Search the C++ standard usage */ + char *std_list = strstr((char *)decl_str, "_List_base"); + if (std_list) + { + /* search a prefix */ + char *cxx_str = strstr((char *)decl_str, "__cxx11"); + if (cxx_str && (cxx_str < std_list)) + { + annobin_gnu_compiler_flags |= GNU_PROPERTY_USECXX11_ABI; + annobin_gnu_compiler_flags_sets |= GNU_PROPERTY_USECXX11_ABI; + } + else + { + annobin_gnu_compiler_flags &= ~GNU_PROPERTY_USECXX11_ABI; + annobin_gnu_compiler_flags_sets |= GNU_PROPERTY_USECXX11_ABI; + } + } + } +} + +static void +annobin_prepare_dynamic_notes (void) +{ + unsigned int i; + + /* Setup default values: */ + /* Mark the validation bit for CXX ABI. */ + if (ANNOBIN_CXXABI_VALIDATION) + { + annobin_gnu_compiler_flags |= GNU_PROPERTY_USECXX_VALIDATION; + annobin_inform(1, "Enable cxxabi validation"); + } + + /* CXX11 ABI is default value since GCC 5.x. */ + annobin_gnu_compiler_flags |= GNU_PROPERTY_USECXX11_ABI; + + /* Check sanitization options. */ + /* Validation bit for linker. */ + + if (ANNOBIN_SANITIZER_VALIDATION); + { + annobin_gnu_compiler_flags |= GNU_PROPERTY_SANITIZE_VALIDATION; + annobin_inform(1, "Enable sanitizer validation"); + } + + /* ASan. */ + if (flag_sanitize & SANITIZE_ADDRESS) + { + annobin_gnu_compiler_flags |= GNU_PROPERTY_SANITIZE_ADDRESS; + annobin_inform(1, "ASan enabled"); + } + /* UBSan, only -fsanitize=undefined option. */ + if (flag_sanitize & SANITIZE_UNDEFINED) + { + annobin_gnu_compiler_flags |= GNU_PROPERTY_SANITIZE_UNDEFINED; + annobin_inform(1, "UBSan enabled"); + } + /* TSan. */ + if (flag_sanitize & SANITIZE_THREAD) + { + annobin_gnu_compiler_flags |= GNU_PROPERTY_SANITIZE_THREAD; + annobin_inform(1, "TSan enabled"); + } +} + +static void +annobin_create_loader_notes (void) +{ + char buffer [1024]; /* FIXME: Is this enough ? */ + char * ptr, * target_ptr; + + annobin_inform (1, "Creating notes for the dynamic loader"); + + /* For now both 32/64 bit .subsections created by annobin_output_note are + 4-bytes aliged: + + ... + Note we use 4-byte alignment even on 64-bit targets. This might seem + wrong for 64-bit systems, but the ELF standard does not specify any + alignment requirements for notes, and it matches already established + practice for other types of notes. + ... + + But recent gABI addition defines that .note.gnu.property section has different + alignments and section sizes for 32-bit and 64-bit ELF binaries. So we need to + ensure binutils support for note section alignment and remove hard-align + 4-byte sections. + + fprintf (asm_out_file, "\t.section %s, \"a\", %%note\n", NOTE_GNU_PROPERTY_SECTION_NAME); + if (annobin_is_64bit) + fprintf (asm_out_file, "\t.balign 8\n"); + else + fprintf (asm_out_file, "\t.balign 4\n"); + */ + + ptr = buffer; + + annobin_inform (1, "Record loader notes: compiler_flags:%u", annobin_gnu_compiler_flags); + + if (annobin_is_64bit) + { + Elf64_32_loader_note note32; + note32.pr_datasz = sizeof (note32.pr_data); + note32.pr_pad = 0; + + if (annobin_enable_stack_size_notes) + { + Elf64_loader_note note64; + + note64.pr_type = GNU_PROPERTY_STACK_SIZE; + note64.pr_data = annobin_max_stack_size; + memcpy (ptr, & note64, sizeof note64); + ptr += sizeof (note64); + } + + note32.pr_type = GNU_PROPERTY_COMPILER_FLAGS; + note32.pr_data = (unsigned int) annobin_gnu_compiler_flags; + memcpy (ptr, & note32, sizeof note32); + ptr += sizeof (note32); + } + else + { + Elf32_loader_note note32; + + note32.pr_datasz = sizeof (note32.pr_data); + + if (annobin_enable_stack_size_notes) + { + note32.pr_type = GNU_PROPERTY_STACK_SIZE; + note32.pr_data = annobin_max_stack_size; + memcpy (ptr, & note32, sizeof note32); + ptr += sizeof (note32); + } + + note32.pr_type = GNU_PROPERTY_COMPILER_FLAGS; + note32.pr_data = annobin_gnu_compiler_flags; + memcpy (ptr, & note32, sizeof note32); + ptr += sizeof (note32); + } + + /* + target_ptr = annobin_target_specific_loader_notes (ptr, (size_t)(ptr - buffer)); + if (target_ptr) { + ptr = target_ptr; + } + */ + + annobin_output_note ("GNU", 4, true, "Loader notes", buffer, NULL, ptr - buffer, + false, NT_GNU_PROPERTY_TYPE_0, NOTE_GNU_PROPERTY_SECTION_NAME); +} + +void +annobin_create_final_notes () +{ + if (asm_out_file == NULL) + return; + + if (annobin_static_notes_p ()) + { + /* It is possible that there is no code in the .text section. + Eg because the compilation was run with the -ffunction-sections option. + Nevertheless we generate this symbol because it is needed by the + version note that was generated in annobin_create_global_notes(). */ + if (annobin_enable_attach) + emit_queued_attachments (); + + annobin_emit_end_symbol (""); + + if (annobin_enable_stack_size_notes && annobin_total_static_stack_usage) + { + annobin_inform(1, "Recording total static usage of %ld", + annobin_total_static_stack_usage); + + annobin_output_numeric_note(GNU_BUILD_ATTRIBUTE_STACK_SIZE, + annobin_total_static_stack_usage, + "numeric: stack-size", NULL, NULL, + NT_GNU_BUILD_ATTRIBUTE_OPEN, GNU_BUILD_ATTRS_SECTION_NAME); + } + } + + if (annobin_dynamic_notes_p ()) + { + annobin_prepare_dynamic_notes (); + annobin_create_loader_notes (); + } +} + +void +annobin_assemble_start_function_notes (void) +{ + if (!annobin_static_notes_p ()) + return; + section *fnsec = function_section (current_function_decl); + annobin_create_function_notes (); +} + +void +annobin_assemble_end_function_notes (void) +{ + if (!annobin_static_notes_p ()) + return; + annobin_create_function_end_symbol (); +} + +void +annobin_init (void) +{ + if (!annobin_enable_p ()) + return; + + if (asm_out_file == NULL) + { + /* This happens during LTO compilation. Compilation is triggered + before any output file has been opened. Since we do not have + the file handle we cannot emit any notes. On the other hand, + the recompilation process will repeat later on with a real + output file and so the notes can be generated then. */ + annobin_inform (1, "Output file not available - unable to generate notes"); + return; + } + + /* Output a file name symbol to be referenced by the notes... */ + if (annobin_current_filename == NULL) + init_annobin_current_filename (); + if (annobin_current_filename == NULL) + { + ice ("Could not find output filename"); + /* We need a filename, so invent one. */ + annobin_current_filename = (char *) "unknown_source"; + } + + annobin_record_global_options (); + if (annobin_static_notes_p()) + annobin_create_global_build_attributes (); +} + +void +annobin_finish_file (void) +{ + if (!annobin_enable_p ()) + return; + annobin_create_final_notes (); +} diff --git a/gcc/annobin.h b/gcc/annobin.h new file mode 100644 index 0000000..d34c0b7 --- /dev/null +++ b/gcc/annobin.h @@ -0,0 +1,152 @@ +/* annobin - Header file for the gcc plugin for annotating binary files. + Copyright (c) 2017 - 2019 Red Hat. + Created by Nick Clifton. + + This 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. + + It 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. */ + +#ifndef TREE_ANNOBIN +#define TREE_ANNOBIN + +/* TODO: replace with host-target width */ +#include + +/* The version of the annotation specification supported by this plugin. */ +#define SPEC_VERSION 3 + +#if 0 /* This would be the correct thing to do if elf/common.h did not conflict with elf.h. */ +#include "elf/common.h" +#else +#define SHF_GNU_BUILD_NOTE (1 << 20) /* Section contains GNU BUILD ATTRIBUTE notes. */ +#define NT_GNU_PROPERTY_TYPE_0 5 /* Generated by gcc. */ + +#define NT_GNU_BUILD_ATTRIBUTE_OPEN 0x100 +#define NT_GNU_BUILD_ATTRIBUTE_FUNC 0x101 + +#define GNU_BUILD_ATTRIBUTE_TYPE_NUMERIC '*' +#define GNU_BUILD_ATTRIBUTE_TYPE_STRING '$' +#define GNU_BUILD_ATTRIBUTE_TYPE_BOOL_TRUE '+' +#define GNU_BUILD_ATTRIBUTE_TYPE_BOOL_FALSE '!' + +#define GNU_BUILD_ATTRIBUTE_VERSION 1 +#define GNU_BUILD_ATTRIBUTE_STACK_PROT 2 +#define GNU_BUILD_ATTRIBUTE_RELRO 3 +#define GNU_BUILD_ATTRIBUTE_STACK_SIZE 4 +#define GNU_BUILD_ATTRIBUTE_TOOL 5 +#define GNU_BUILD_ATTRIBUTE_ABI 6 +#define GNU_BUILD_ATTRIBUTE_PIC 7 +#define GNU_BUILD_ATTRIBUTE_SHORT_ENUM 8 +#define GNU_BUILD_ATTRIBUTE_NO_SANITIZE 9 + +#define NOTE_GNU_PROPERTY_SECTION_NAME ".note.gnu.property" +#define GNU_BUILD_ATTRS_SECTION_NAME ".gnu.build.attributes" + +/* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0). */ +#define GNU_PROPERTY_STACK_SIZE 1 +#define GNU_PROPERTY_NO_COPY_ON_PROTECTED 2 +#define GNU_PROPERTY_COMPILER_FLAGS 32 +/* Bit masks for compiler flags: */ +/* Pre/post cxx11 ABI. */ +#define GNU_PROPERTY_USECXX_VALIDATION (1U << 0) +#define GNU_PROPERTY_USECXX11_ABI (1U << 1) +/* Sanitizer flags. */ +#define GNU_PROPERTY_SANITIZE_VALIDATION (1U << 2) +#define GNU_PROPERTY_SANITIZE_ADDRESS (1U << 3) +#define GNU_PROPERTY_SANITIZE_UNDEFINED (1U << 4) +#define GNU_PROPERTY_SANITIZE_THREAD (1U << 5) +#endif /* Copy of elf/common.h */ + +typedef struct +{ + Elf32_Word pr_type; + Elf32_Word pr_datasz; + Elf32_Word pr_data; +} Elf32_loader_note; + +typedef struct +{ + Elf32_Word pr_type; + Elf32_Word pr_datasz; + Elf64_Xword pr_data; +} Elf64_loader_note; + +typedef struct +{ + Elf32_Word pr_type; + Elf32_Word pr_datasz; + Elf32_Word pr_data; + Elf32_Word pr_pad; +} Elf64_32_loader_note; + +/* Called during plugin_init(). */ +extern void annobin_save_target_specific_information (void); + +/* Called during PLUGIN_START_UNIT. + Should only produce notes for the static tools, ie + notes in the SECNAME section. */ +extern void annobin_record_global_target_notes (const char * SECNAME); + +/* Called during PLUGIN_ALL_PASSES_START. + Should produce notes specific to the function just compiled. + Should only produce notes for the static tools, ie + notes in the .gnu.build.attributes section. + Arguments are the START and END symbols for the function, + the name of the note SECTION into which the notes should be + placed and a boolean indicating if it is necessary to FORCE + the generation of notes even if nothing has changed. */ +extern void annobin_target_specific_function_notes (const char * START, + const char * END, + const char * SECTION, + bool FORCE); + +/* Called during PLUGIN_FINISH_UNIT. + Should only produce notes for the dynamic loader, ie + notes in the .note.gnu.property section. */ +extern void annobin_target_specific_loader_notes (void); + +/* Called during plugin_init (). + Returns the bias, if any, that should be applied to + the start symbol in order for it to avoid conflicts + with file symbols and/or the first function symbol. */ +extern signed int annobin_target_start_symbol_bias (void); + +extern void annobin_assemble_start_function_notes (); +extern void annobin_assemble_end_function_notes (); + +/* Generate final annobin annotations. */ +extern void annobin_finish_file (); + +extern void annobin_search_abi_declaration (tree decl); + +/* Declare initial static sections for now, compute flags. */ +extern void annobin_init (); + +/* Check if binary annotations are enabled. */ +static inline bool +annobin_enable_p (void) +{ + return (flag_annotate_binary != ANNOBIN_NONE); +} + +/* Check if .note.gnu.property annotations are enabled. */ +static inline bool +annobin_dynamic_notes_p (void) +{ + return (flag_annotate_binary & ANNOBIN_DYNAMIC_NOTES); +} + +/* Check if .gnu.build.attributes annotations are enabled. */ +static inline bool +annobin_static_notes_p (void) +{ + return (flag_annotate_binary & ANNOBIN_STATIC_NOTES); +} + +#endif /* TREE_ANNOBIN */ diff --git a/gcc/asan.h b/gcc/asan.h index 86b2955..efc517e 100644 --- a/gcc/asan.h +++ b/gcc/asan.h @@ -157,4 +157,24 @@ asan_protect_stack_decl (tree decl) || (asan_sanitize_use_after_scope () && TREE_ADDRESSABLE (decl))); } +/* Return true when flag_sanitize & FLAG is non-zero. If FN is non-null, + remove all flags mentioned in "no_sanitize" of DECL_ATTRIBUTES. */ + +static inline bool +sanitize_flags_p (unsigned int flag, const_tree fn = current_function_decl) +{ + unsigned int result_flags = flag_sanitize & flag; + if (result_flags == 0) + return false; + + if (fn != NULL_TREE) + { + tree value = lookup_attribute ("no_sanitize", DECL_ATTRIBUTES (fn)); + if (value) + result_flags &= ~tree_to_uhwi (TREE_VALUE (value)); + } + + return result_flags; +} + #endif /* TREE_ASAN */ diff --git a/gcc/common.opt b/gcc/common.opt index 44292ad..0b78f24 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -928,6 +928,25 @@ Align the start of loops. falign-loops= Common RejectNegative Joined UInteger Var(align_loops) +fannobin= +Common Driver Joined Report RejectNegative Enum(annobin_notes_type) Var(flag_annotate_binary) Init(ANNOBIN_NONE) +Enable binary annotations with dynamic (.note.gnu.property) or/and static (.gnu.build.attributes) ELF notes. + +Enum +Name(annobin_notes_type) Type(enum annobin_notes_type) UnknownError(unknown binary annotaion type %qs) + +EnumValue +Enum(annobin_notes_type) String(none) Value(ANNOBIN_NONE) + +EnumValue +Enum(annobin_notes_type) String(dynamic) Value(ANNOBIN_DYNAMIC_NOTES) + +EnumValue +Enum(annobin_notes_type) String(static) Value(ANNOBIN_STATIC_NOTES) + +EnumValue +Enum(annobin_notes_type) String(all) Value(ANNOBIN_ALL) + fargument-alias Common Ignore Does nothing. Preserved for backward compatibility. diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 95d7cf0..5b5505c 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see #include "plugin.h" #include "cilk.h" #include "builtins.h" +#include "annobin.h" /* Possible cases of bad specifiers type used by bad_specifiers. */ enum bad_spec_place { @@ -7015,6 +7016,7 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, if (was_readonly) TREE_READONLY (decl) = 1; + annobin_search_abi_declaration (decl); invoke_plugin_callbacks (PLUGIN_FINISH_DECL, decl); } diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 2a3a48e..559bed6 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -324,5 +324,14 @@ enum gfc_convert GFC_FLAG_CONVERT_LITTLE }; +/* Annobin recorded notes type. */ +enum annobin_notes_type +{ + ANNOBIN_NONE = 0UL, + ANNOBIN_DYNAMIC_NOTES = 1UL << 0, + ANNOBIN_STATIC_NOTES = 1UL << 1, + ANNOBIN_ALL = ANNOBIN_DYNAMIC_NOTES | ANNOBIN_STATIC_NOTES +}; + #endif /* ! GCC_FLAG_TYPES_H */ diff --git a/gcc/output.h b/gcc/output.h index 0924499..d9507b9 100644 --- a/gcc/output.h +++ b/gcc/output.h @@ -535,6 +535,7 @@ extern section *mergeable_constant_section (machine_mode, extern section *function_section (tree); extern section *unlikely_text_section (void); extern section *current_function_section (void); +extern const char * current_function_default_section_suffix (void); /* Return the numbered .ctors.N (if CONSTRUCTOR_P) or .dtors.N (if not) section for PRIORITY. */ diff --git a/gcc/params.def b/gcc/params.def index c1d2e7b..3696af6 100644 --- a/gcc/params.def +++ b/gcc/params.def @@ -1220,6 +1220,21 @@ DEFPARAM (PARAM_MAX_SPECULATIVE_DEVIRT_MAYDEFS, "Maximum number of may-defs visited when devirtualizing " "speculatively", 50, 0, 0) +DEFPARAM(PARAM_ANNOBIN_SANITIZER_VALIDATION, + "annobin-sanitizer-validation", + "Set up bit for sanitizer validaion in dynamic notes for linker verification.", + 1, 0, 1) + +DEFPARAM(PARAM_ANNOBIN_CXXABI_VALIDATION, + "annobin-cxxabi-validation", + "Set up bit for cxxabi validaion in dynamic notes for linker verification.", + 1, 0, 1) + +DEFPARAM(PARAM_ANNOBIN_VERBOSE, + "annobin-verbose", + "Enable verbose output for annobin.", + 0, 0, 2) + /* Local variables: diff --git a/gcc/params.h b/gcc/params.h index 4017c85..f943a4a 100644 --- a/gcc/params.h +++ b/gcc/params.h @@ -245,5 +245,11 @@ extern void init_param_values (int *params); PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD) #define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \ ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)) +#define ANNOBIN_SANITIZER_VALIDATION \ + PARAM_VALUE (PARAM_ANNOBIN_SANITIZER_VALIDATION) +#define ANNOBIN_CXXABI_VALIDATION \ + PARAM_VALUE (PARAM_ANNOBIN_CXXABI_VALIDATION) +#define ANNOBIN_VERBOSE \ + PARAM_VALUE (PARAM_ANNOBIN_VERBOSE) #endif /* ! GCC_PARAMS_H */ diff --git a/gcc/testsuite/gcc.dg/annobin/annobin.exp b/gcc/testsuite/gcc.dg/annobin/annobin.exp new file mode 100644 index 0000000..b5db4a8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/annobin/annobin.exp @@ -0,0 +1,35 @@ +# Copyright (C) 2019 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 +# . + +# GCC testsuite that uses the `dg.exp' driver. + +# Load support procs. +load_lib gcc-dg.exp + +# If a testcase doesn't have special options, use these. +global DEFAULT_CFLAGS +if ![info exists DEFAULT_CFLAGS] then { + set DEFAULT_CFLAGS " -ansi -pedantic-errors" +} + +# Initialize `dg'. +dg-init + +# Main loop. +dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[c\]]] "" $DEFAULT_CFLAGS + +# All done. +dg-finish diff --git a/gcc/testsuite/gcc.dg/annobin/build-attributes-1.c b/gcc/testsuite/gcc.dg/annobin/build-attributes-1.c new file mode 100644 index 0000000..0bede41 --- /dev/null +++ b/gcc/testsuite/gcc.dg/annobin/build-attributes-1.c @@ -0,0 +1,4 @@ +/* { dg-do compile } */ +/* { dg-options "-fannobin=static" } */ +/* { dg-final { scan-assembler "\.gnu\.build\.attributes" } } */ +/* { dg-final { scan-assembler-not "\.note\.gnu\.property" } } */ diff --git a/gcc/testsuite/gcc.dg/annobin/build-attributes-2.c b/gcc/testsuite/gcc.dg/annobin/build-attributes-2.c new file mode 100644 index 0000000..73f5a79 --- /dev/null +++ b/gcc/testsuite/gcc.dg/annobin/build-attributes-2.c @@ -0,0 +1,5 @@ +/* { dg-do compile } */ +/* { dg-options "-fannobin=static" } */ + +/* { dg-final { scan-assembler "\.(push)section \.gnu\.build\.attributes" } } */ +void foo() {} diff --git a/gcc/testsuite/gcc.dg/annobin/build-attributes-3.c b/gcc/testsuite/gcc.dg/annobin/build-attributes-3.c new file mode 100644 index 0000000..2532b34 --- /dev/null +++ b/gcc/testsuite/gcc.dg/annobin/build-attributes-3.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fannobin=static" } */ +/* { dg-additional-options "-freorder-functions -ffunction-sections -O1" } */ + +/* { dg-final { scan-assembler "\.(push)section \.gnu\.build\.attributes\.text\.foo\.hot" } } */ +__attribute__((hot)) +void foo() {} + +/* { dg-final { scan-assembler "\.(push)section \.gnu\.build\.attributes\.text\.bar\.unlikely" } } */ +__attribute__((cold)) +void bar() {} + +/* { dg-final { scan-assembler "\.(push)section \.gnu\.build\.attributes\.custom_section" } } */ +__attribute__((section(".custom_section"))) +void baz() {} + +// TODO: startup/exit suffixes diff --git a/gcc/testsuite/gcc.dg/annobin/build-attributes-4.c b/gcc/testsuite/gcc.dg/annobin/build-attributes-4.c new file mode 100644 index 0000000..3b244d9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/annobin/build-attributes-4.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fannobin=static" } */ +/* { dg-additional-options "-freorder-functions -ffunction-sections -O1" } */ + +/* { dg-final { scan-assembler "\.(push)section \.gnu\.build\.attributes\.text\.ct\.startup" } } */ +__attribute__((constructor)) +void ct() {} + +/* { dg-final { scan-assembler "\.(push)section \.gnu\.build\.attributes\.text\.dt\.exit" } } */ +__attribute__((destructor)) +void dt() {} + +/* { dg-final { scan-assembler "\.(push)section \.gnu\.build\.attributes\.text\.main" } } */ +int main() {} diff --git a/gcc/testsuite/gcc.dg/annobin/build-attributes-5.c b/gcc/testsuite/gcc.dg/annobin/build-attributes-5.c new file mode 100644 index 0000000..ee843f2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/annobin/build-attributes-5.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-fannobin=static -fverbose-asm" } */ +/* { dg-additional-options "-fsanitize=address" } */ + + +/* { dg-final { scan-assembler "\.(push)section \.gnu\.build\.attributes" } } */ +/* { dg-final { scan-assembler-times "no_sanitize:\ true" 2} } */ + +/* 2 unsanitized functions are expected: foo and static ctor with asan_init. */ +__attribute__((no_sanitize_address)) +void foo() {} diff --git a/gcc/testsuite/gcc.dg/annobin/note-property-1.c b/gcc/testsuite/gcc.dg/annobin/note-property-1.c new file mode 100644 index 0000000..3581168 --- /dev/null +++ b/gcc/testsuite/gcc.dg/annobin/note-property-1.c @@ -0,0 +1,6 @@ +/* { dg-do compile } */ +/* { dg-options "-fannobin=dynamic -fverbose-asm" } */ +/* { dg-final { scan-assembler "\.note\.gnu\.property" } } */ +/* { dg-final { scan-assembler "PROPERTY_TYPE_0" } } */ +/* { dg-final { scan-assembler "\.(asci(i|z)|string) \"GNU\"" } } */ +/* { dg-final { scan-assembler-not "\.gnu\.build\.attributes" } } */ diff --git a/gcc/testsuite/gcc.dg/annobin/note-property-2.c b/gcc/testsuite/gcc.dg/annobin/note-property-2.c new file mode 100644 index 0000000..5530307 --- /dev/null +++ b/gcc/testsuite/gcc.dg/annobin/note-property-2.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ +/* { dg-options "-fannobin=dynamic" } */ +/* { dg-final { scan-assembler "\.note\.gnu\.property" } } */ +/* { dg-final { scan-assembler "\.(asci(i|z)|string) \"GNU\"" } } */ + +void foo() {} +void bar() {} diff --git a/gcc/toplev.c b/gcc/toplev.c index 39692c4..68a1085 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -77,6 +77,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-chkp.h" #include "omp-low.h" #include "hsa.h" +#include "annobin.h" #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO) #include "dbxout.h" @@ -621,6 +622,9 @@ compile_file (void) /* Invoke registered plugin callbacks. */ invoke_plugin_callbacks (PLUGIN_FINISH_UNIT, NULL); + /* Generate final binary annotations . */ + annobin_finish_file (); + /* This must be at the end. Some target ports emit end of file directives into the assembly file here, and hence we can not output anything to the assembly file after this point. */ @@ -1991,6 +1995,7 @@ do_compile () init_final (main_input_filename); coverage_init (aux_base_name); statistics_init (); + annobin_init (); invoke_plugin_callbacks (PLUGIN_START_UNIT, NULL); timevar_stop (TV_PHASE_SETUP); diff --git a/gcc/varasm.c b/gcc/varasm.c index 2c48b93..1445794 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -52,6 +52,7 @@ along with GCC; see the file COPYING3. If not see #include "common/common-target.h" #include "asan.h" #include "rtl-iter.h" +#include "annobin.h" #ifdef XCOFF_DEBUGGING_INFO #include "xcoffout.h" /* Needed for external data declarations. */ @@ -545,20 +546,12 @@ get_named_text_section (tree decl, return get_named_section (decl, text_section_name, 0); } -/* Choose named function section based on its frequency. */ +/* Get default section suffix for function. */ -section * -default_function_section (tree decl, enum node_frequency freq, - bool startup, bool exit) +const char * +default_function_section_suffix (tree decl, enum node_frequency freq, + bool startup, bool exit) { -#if defined HAVE_LD_EH_GC_SECTIONS && defined HAVE_LD_EH_GC_SECTIONS_BUG - /* Old GNU linkers have buggy --gc-section support, which sometimes - results in .gcc_except_table* sections being garbage collected. */ - if (decl - && symtab_node::get (decl)->implicit_section) - return NULL; -#endif - if (!flag_reorder_functions || !targetm_common.have_named_sections) return NULL; @@ -570,31 +563,52 @@ default_function_section (tree decl, enum node_frequency freq, /* If we do have a profile or(and) LTO phase is executed, we do not need these ELF section. */ if (!in_lto_p || !flag_profile_values) - return get_named_text_section (decl, ".text.startup", NULL); + return ".startup"; else return NULL; } /* Similarly for exit. */ if (exit && freq != NODE_FREQUENCY_UNLIKELY_EXECUTED) - return get_named_text_section (decl, ".text.exit", NULL); + return ".exit"; /* Group cold functions together, similarly for hot code. */ switch (freq) { case NODE_FREQUENCY_UNLIKELY_EXECUTED: - return get_named_text_section (decl, ".text.unlikely", NULL); + return ".unlikely"; case NODE_FREQUENCY_HOT: /* If we do have a profile or(and) LTO phase is executed, we do not need these ELF section. */ if (!in_lto_p || !flag_profile_values) - return get_named_text_section (decl, ".text.hot", NULL); + return ".hot"; /* FALLTHRU */ default: return NULL; } } +/* Choose named function section based on its frequency. */ + +section * +default_function_section (tree decl, enum node_frequency freq, + bool startup, bool exit) +{ +#if defined HAVE_LD_EH_GC_SECTIONS && defined HAVE_LD_EH_GC_SECTIONS_BUG + /* Old GNU linkers have buggy --gc-section support, which sometimes + results in .gcc_except_table* sections being garbage collected. */ + if (decl + && symtab_node::get (decl)->implicit_section) + return NULL; +#endif + const char * suffix; + suffix = default_function_section_suffix (decl, freq, startup, exit); + if (!suffix) + return NULL; + + return get_named_text_section (decl, ".text", suffix); +} + /* Return the section for function DECL. If DECL is NULL_TREE, return the text section. We can be passed @@ -674,6 +688,30 @@ current_function_section (void) return function_section_1 (current_function_decl, in_cold_section_p); } +const char * +current_function_default_section_suffix (void) +{ + /* TODO: this Dublicates function_section_1. */ + enum node_frequency freq = NODE_FREQUENCY_NORMAL; + /* TODO: check if we already have funcs for curren? Mostly like have */ + bool startup = false, exit = false; + + if (current_function_decl) + { + struct cgraph_node *node = cgraph_node::get (current_function_decl); + + if (node) + { + freq = node->frequency; + startup = node->only_called_at_startup; + exit = node->only_called_at_exit; + } + } + + return default_function_section_suffix(current_function_decl, freq, startup, + exit); +} + /* Tell assembler to switch to unlikely-to-be-executed text section. */ section * @@ -1723,6 +1761,8 @@ assemble_start_function (tree decl, const char *fnname) app_disable (); + annobin_assemble_start_function_notes (); + if (CONSTANT_POOL_BEFORE_FUNCTION) output_constant_pool (fnname, decl); @@ -1861,6 +1901,8 @@ assemble_end_function (tree decl, const char *fnname ATTRIBUTE_UNUSED) ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.hot_section_end_label); switch_to_section (save_text_section); } + + annobin_assemble_end_function_notes (); } /* Assemble code to leave SIZE bytes of zeros. */ -- 2.7.4 From 6a8a3da5ad28e8460d0de79c329c2cb7fbad6b93 Mon Sep 17 00:00:00 2001 From: marxin Date: Mon, 3 Jul 2017 09:26:31 +0000 Subject: [PATCH 15/16] Make stack epilogue more efficient 2017-07-03 Martin Liska * asan.c (asan_emit_stack_protection): Unpoison just red zones and shadow memory of auto variables which are subject of use-after-scope sanitization. (asan_expand_mark_ifn): Add do set only when is_poison. (cherry picked 355c17620ce) Change-Id: Ib36a512acb3e0dcb9e7dbfbd9a55661694e7e749 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@249900 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 7 ++++++ gcc/asan.c | 79 ++++++++++++++++++++++++++++++----------------------------- 2 files changed, 47 insertions(+), 39 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 10d69ee..c5a8c80c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2017-07-03 Martin Liska + + * asan.c (asan_emit_stack_protection): Unpoison just red zones + and shadow memory of auto variables which are subject of + use-after-scope sanitization. + (asan_expand_mark_ifn): Add do set only when is_poison. + 2018-01-01 Jakub Jelinek PR middle-end/83623 diff --git a/gcc/asan.c b/gcc/asan.c index 0f5419f..20eae75 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -1071,7 +1071,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, HOST_WIDE_INT base_offset = offsets[length - 1]; HOST_WIDE_INT base_align_bias = 0, offset, prev_offset; HOST_WIDE_INT asan_frame_size = offsets[0] - base_offset; - HOST_WIDE_INT last_offset; + HOST_WIDE_INT last_offset, last_size; int l; unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT; tree str_cst, decl, id; @@ -1306,58 +1306,55 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, if (STRICT_ALIGNMENT) set_mem_align (shadow_mem, (GET_MODE_ALIGNMENT (SImode))); - /* Unpoison shadow memory of a stack at the very end of a function. - As we're poisoning stack variables at the end of their scope, - shadow memory must be properly unpoisoned here. The easiest approach - would be to collect all variables that should not be unpoisoned and - we unpoison shadow memory of the whole stack except ranges - occupied by these variables. */ + prev_offset = base_offset; last_offset = base_offset; - HOST_WIDE_INT current_offset = last_offset; - if (length) + last_size = 0; + for (l = length; l; l -= 2) { - HOST_WIDE_INT var_end_offset = 0; - HOST_WIDE_INT stack_start = offsets[length - 1]; - gcc_assert (last_offset == stack_start); - - for (int l = length - 2; l > 0; l -= 2) + offset = base_offset + ((offsets[l - 1] - base_offset) + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); + if (last_offset + last_size != offset) { - HOST_WIDE_INT var_offset = offsets[l]; - current_offset = var_offset; - var_end_offset = offsets[l - 1]; - HOST_WIDE_INT rounded_size = ROUND_UP (var_end_offset - var_offset, - BITS_PER_UNIT); + shadow_mem = adjust_address (shadow_mem, VOIDmode, + (last_offset - prev_offset) + >> ASAN_SHADOW_SHIFT); + prev_offset = last_offset; + asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT); + last_offset = offset; + last_size = 0; + } + last_size += base_offset + ((offsets[l - 2] - base_offset) + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)) + - offset; - /* Should we unpoison the variable? */ + /* Unpoison shadow memory that corresponds to a variable that is + is subject of use-after-return sanitization. */ + if (l > 2) + { + decl = decls[l / 2 - 2]; if (asan_handled_variables != NULL && asan_handled_variables->contains (decl)) { + HOST_WIDE_INT size = offsets[l - 3] - offsets[l - 2]; if (dump_file && (dump_flags & TDF_DETAILS)) { const char *n = (DECL_NAME (decl) ? IDENTIFIER_POINTER (DECL_NAME (decl)) : ""); fprintf (dump_file, "Unpoisoning shadow stack for variable: " - "%s (%" PRId64 "B)\n", n, - var_end_offset - var_offset); + "%s (%" PRId64 " B)\n", n, size); } - unsigned HOST_WIDE_INT s - = shadow_mem_size (current_offset - last_offset); - asan_clear_shadow (shadow_mem, s); - HOST_WIDE_INT shift - = shadow_mem_size (current_offset - last_offset + rounded_size); - shadow_mem = adjust_address (shadow_mem, VOIDmode, shift); - last_offset = var_offset + rounded_size; - current_offset = last_offset; + last_size += size & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1); } - } - - /* Handle last redzone. */ - current_offset = offsets[0]; - asan_clear_shadow (shadow_mem, - shadow_mem_size (current_offset - last_offset)); + } + if (last_size) + { + shadow_mem = adjust_address (shadow_mem, VOIDmode, + (last_offset - prev_offset) + >> ASAN_SHADOW_SHIFT); + asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT); } /* Clean-up set with instrumented stack variables. */ @@ -2721,9 +2718,13 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter) gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR); tree decl = TREE_OPERAND (base, 0); gcc_checking_assert (TREE_CODE (decl) == VAR_DECL); - if (asan_handled_variables == NULL) - asan_handled_variables = new hash_set (16); - asan_handled_variables->add (decl); + + if (is_poison) + { + if (asan_handled_variables == NULL) + asan_handled_variables = new hash_set (16); + asan_handled_variables->add (decl); + } tree len = gimple_call_arg (g, 2); gcc_assert (tree_fits_shwi_p (len)); -- 2.7.4 From 8474d5b72c9754d1875750f54554a47d5e85515c Mon Sep 17 00:00:00 2001 From: marxin Date: Tue, 9 Jan 2018 11:47:26 +0000 Subject: [PATCH 16/16] Add gcc_assert about stack alignment (PR sanitizer/82517). 2018-01-09 Martin Liska PR sanitizer/82517 * asan.c (shadow_mem_size): Add gcc_assert. (cherry picked from e56a070cadd948b81a5b769e17c8c1162829de27) Change-Id: I0f97457dfc30b6afbde93ff53e2b5d923946a122 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@256378 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 5 +++++ gcc/asan.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index c5a8c80c..4c738e3 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2018-01-09 Martin Liska + + PR sanitizer/82517 + * asan.c (shadow_mem_size): Add gcc_assert. + 2017-07-03 Martin Liska * asan.c (asan_emit_stack_protection): Unpoison just red zones diff --git a/gcc/asan.c b/gcc/asan.c index 20eae75..cb5a21f 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -1043,6 +1043,11 @@ asan_function_start (void) static unsigned HOST_WIDE_INT shadow_mem_size (unsigned HOST_WIDE_INT size) { + /* It must be possible to align stack variables to granularity + of shadow memory. */ + gcc_assert (BITS_PER_UNIT + * ASAN_SHADOW_GRANULARITY <= MAX_SUPPORTED_STACK_ALIGNMENT); + return ROUND_UP (size, ASAN_SHADOW_GRANULARITY) / ASAN_SHADOW_GRANULARITY; } -- 2.7.4