From 3ba95a5dff386c92dd0399d5a5a6103e19d65cd3 Mon Sep 17 00:00:00 2001 From: Dongkyun Son Date: Wed, 8 Nov 2017 10:49:50 +0900 Subject: [PATCH 01/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 02/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 03/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 04/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 05/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 06/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 From bae5af3a2f828abe0285247958040325283bb34d Mon Sep 17 00:00:00 2001 From: marxin Date: Tue, 9 Oct 2018 08:34:52 +0000 Subject: [PATCH 07/16] ASAN: emit line information of stack variables. 2018-10-09 Martin Liska * asan.c (asan_emit_stack_protection): If a stack variable is located in a same file as current function, then emit line info into variable definition string. 2018-10-09 Martin Liska * c-c++-common/asan/pr64820.c: Add line number to scanned pattern. * c-c++-common/asan/use-after-return-1.c: Likewise. * g++.dg/asan/function-argument-1.C (main): Likewise. * g++.dg/asan/function-argument-2.C (main): Likewise. * g++.dg/asan/function-argument-3.C (main): Likewise. * g++.dg/asan/use-after-scope-1.C (main): Likewise. * g++.dg/asan/use-after-scope-2.C (main): Likewise. * g++.dg/asan/use-after-scope-types-1.C (main): Likewise. * g++.dg/asan/use-after-scope-types-2.C (main): Likewise. * g++.dg/asan/use-after-scope-types-3.C (main): Likewise. * g++.dg/asan/use-after-scope-types-4.C (main): Likewise. * g++.dg/asan/use-after-scope-types-5.C (main): Likewise. * gcc.dg/asan/pr78541.c (main): Likewise. * gcc.dg/asan/use-after-scope-1.c (main): Likewise. * gcc.dg/asan/use-after-scope-10.c (main): Likewise. * gcc.dg/asan/use-after-scope-2.c (main): Likewise. * gcc.dg/asan/use-after-scope-3.c (main): Likewise. * gcc.dg/asan/use-after-scope-5.c (main): Likewise. * gcc.dg/asan/use-after-scope-9.c (main): Likewise. (backported 1db5adee4f01d05f3f1b9c09c04b9b772ace6859 without * g++.dg/asan/function-argument-1.C * g++.dg/asan/function-argument-2.C * g++.dg/asan/function-argument-3.C * gcc.dg/asan/pr78541.c * gcc.dg/asan/use-after-scope-10.c test) Change-Id: I03162f42275c2f3c0b92c6bfb397dbc91229a917 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@264951 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/asan.c | 22 ++++++++++++++++++++-- gcc/testsuite/c-c++-common/asan/pr64820.c | 2 +- .../c-c++-common/asan/use-after-return-1.c | 2 +- gcc/testsuite/g++.dg/asan/use-after-scope-1.C | 2 +- gcc/testsuite/g++.dg/asan/use-after-scope-2.C | 2 +- .../g++.dg/asan/use-after-scope-types-1.C | 2 +- .../g++.dg/asan/use-after-scope-types-2.C | 2 +- .../g++.dg/asan/use-after-scope-types-3.C | 2 +- .../g++.dg/asan/use-after-scope-types-4.C | 2 +- .../g++.dg/asan/use-after-scope-types-5.C | 2 +- gcc/testsuite/gcc.dg/asan/use-after-scope-1.c | 2 +- gcc/testsuite/gcc.dg/asan/use-after-scope-2.c | 2 +- gcc/testsuite/gcc.dg/asan/use-after-scope-3.c | 2 +- gcc/testsuite/gcc.dg/asan/use-after-scope-5.c | 2 +- gcc/testsuite/gcc.dg/asan/use-after-scope-9.c | 2 +- 15 files changed, 34 insertions(+), 16 deletions(-) diff --git a/gcc/asan.c b/gcc/asan.c index cb5a21f..11c2b52 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -1085,6 +1085,9 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, if (shadow_ptr_types[0] == NULL_TREE) asan_init_shadow_ptr_types (); + expanded_location cfun_xloc + = expand_location (DECL_SOURCE_LOCATION (current_function_decl)); + /* First of all, prepare the description string. */ pretty_printer asan_pp; @@ -1097,15 +1100,30 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, pp_space (&asan_pp); pp_wide_integer (&asan_pp, offsets[l - 1] - offsets[l]); pp_space (&asan_pp); + + expanded_location xloc + = expand_location (DECL_SOURCE_LOCATION (decl)); + char location[32]; + + if (xloc.file == cfun_xloc.file) + sprintf (location, ":%d", xloc.line); + else + location[0] = '\0'; + if (DECL_P (decl) && DECL_NAME (decl)) { - pp_decimal_int (&asan_pp, IDENTIFIER_LENGTH (DECL_NAME (decl))); + unsigned idlen + = IDENTIFIER_LENGTH (DECL_NAME (decl)) + strlen (location); + pp_decimal_int (&asan_pp, idlen); pp_space (&asan_pp); pp_tree_identifier (&asan_pp, DECL_NAME (decl)); + pp_string (&asan_pp, location); } else pp_string (&asan_pp, "9 "); - pp_space (&asan_pp); + + if (l > 2) + pp_space (&asan_pp); } str_cst = asan_pp_string (&asan_pp); diff --git a/gcc/testsuite/c-c++-common/asan/pr64820.c b/gcc/testsuite/c-c++-common/asan/pr64820.c index c42f608..40cc573 100644 --- a/gcc/testsuite/c-c++-common/asan/pr64820.c +++ b/gcc/testsuite/c-c++-common/asan/pr64820.c @@ -28,4 +28,4 @@ int main(int argc, char **argv) { /* { dg-output "\[^\n\r]*WRITE of size 1 at .* thread T0.*" } */ /* { dg-output " #0.*(Func2)?.*pr64820.(c:21)?.*" } */ /* { dg-output "is located in stack of thread T0 at offset.*" } */ -/* { dg-output "\'local\'\[^\n\r]*<== Memory access at offset 32 is inside this variable" } */ +/* { dg-output "\'local\'\[^\n\r]* \\(line 14\\) <== Memory access at offset 32 is inside this variable" } */ diff --git a/gcc/testsuite/c-c++-common/asan/use-after-return-1.c b/gcc/testsuite/c-c++-common/asan/use-after-return-1.c index 49933e5..e1bb18a 100644 --- a/gcc/testsuite/c-c++-common/asan/use-after-return-1.c +++ b/gcc/testsuite/c-c++-common/asan/use-after-return-1.c @@ -50,4 +50,4 @@ int main(int argc, char **argv) { /* { dg-output "WRITE of size 1 at .* thread T0.*" } */ /* { dg-output " #0.*(Func2)?.*use-after-return-1.(c:31)?.*" } */ /* { dg-output "is located in stack of thread T0 at offset.*" } */ -/* { dg-output "\'local\' <== Memory access at offset 32 is inside this variable" } */ +/* { dg-output "\'local\' \\(line 24\\) <== Memory access at offset 32 is inside this variable" } */ diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-1.C b/gcc/testsuite/g++.dg/asan/use-after-scope-1.C index fd875ad..4cbc534 100644 --- a/gcc/testsuite/g++.dg/asan/use-after-scope-1.C +++ b/gcc/testsuite/g++.dg/asan/use-after-scope-1.C @@ -18,4 +18,4 @@ int main() { // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "READ of size 4 at.*" } -// { dg-output ".*'v' <== Memory access at offset \[0-9\]* is inside this variable.*" } +// { dg-output ".*'v' \\(line 9\\) <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-2.C b/gcc/testsuite/g++.dg/asan/use-after-scope-2.C index 92a4bd1..5d11834 100644 --- a/gcc/testsuite/g++.dg/asan/use-after-scope-2.C +++ b/gcc/testsuite/g++.dg/asan/use-after-scope-2.C @@ -37,4 +37,4 @@ int main(int argc, char **argv) // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "READ of size 4 at.*" } -// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" } +// { dg-output ".*'x' \\(line 31\\) <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C index bedcfa4..180804c 100644 --- a/gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C +++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C @@ -14,4 +14,4 @@ int main() // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "WRITE of size " } -// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" } +// { dg-output ".*'x' \\(line 25\\) <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C index 75a01d9..172c5c0 100644 --- a/gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C +++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C @@ -14,4 +14,4 @@ int main() // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "WRITE of size " } -// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" } +// { dg-output ".*'x' \\(line 25\\) <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C index 3350c69..d4ad0fc 100644 --- a/gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C +++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C @@ -14,4 +14,4 @@ int main() // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "WRITE of size " } -// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" } +// { dg-output ".*'x' \\(line 25\\) <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C index 44f4d3b..7638107 100644 --- a/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C +++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C @@ -14,4 +14,4 @@ int main() // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "READ of size " } -// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" } +// { dg-output ".*'x' \\(line 25\\) <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C index 42abc2a..fe7c57f 100644 --- a/gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C +++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C @@ -14,4 +14,4 @@ int main() // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "WRITE of size " } -// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" } +// { dg-output ".*'x' \\(line 25\\) <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c index bdbc97b..19a8379 100644 --- a/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c @@ -15,4 +15,4 @@ main (void) // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "READ of size 1 at.*" } -// { dg-output ".*'my_char' <== Memory access at offset \[0-9\]* is inside this variable.*" } +// { dg-output ".*'my_char' \\(line 9\\) <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c index dedb734..1018581 100644 --- a/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c @@ -44,4 +44,4 @@ main (void) // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "READ of size 4 at.*" } -// { dg-output ".*'c' <== Memory access at offset \[0-9\]* is inside this variable.*" } +// { dg-output ".*'c' \\(line 37\\) <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c index 9aeed51..838e421 100644 --- a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c @@ -17,4 +17,4 @@ main (void) // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "WRITE of size 1 at.*" } -// { dg-output ".*'my_char' <== Memory access at offset \[0-9\]* overflows this variable.*" } +// { dg-output ".*'my_char' \\(line 11\\) <== Memory access at offset \[0-9\]* overflows this variable.*" } diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c index b53712d..1c2fafb 100644 --- a/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c @@ -24,4 +24,4 @@ main (int argc, char **argv) // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "READ of size 4 at.*" } -// { dg-output ".*'values' <== Memory access at offset \[0-9\]* is inside this variable.*" } +// { dg-output ".*'values' \\(line 10\\) <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c index 2e30def..17c724f 100644 --- a/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c @@ -17,4 +17,4 @@ main (int argc, char **argv) // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "READ of size .*" } -// { dg-output ".*'a' <== Memory access at offset \[0-9\]* is inside this variable.*" } +// { dg-output ".*'a' \\(line 12\\) <== Memory access at offset \[0-9\]* is inside this variable.*" } -- 2.7.4 From 2135d204d97bb02bc9c006efe92ef1347ea0c0d2 Mon Sep 17 00:00:00 2001 From: marxin Date: Fri, 30 Nov 2018 14:25:15 +0000 Subject: [PATCH 08/16] Make red zone size more flexible for stack variables (PR sanitizer/81715). 2018-11-30 Martin Liska PR sanitizer/81715 * asan.c (asan_shadow_cst): Remove, partially transform into flush_redzone_payload. (RZ_BUFFER_SIZE): New. (struct asan_redzone_buffer): New. (asan_redzone_buffer::emit_redzone_byte): Likewise. (asan_redzone_buffer::flush_redzone_payload): Likewise. (asan_redzone_buffer::flush_if_full): Likewise. (asan_emit_stack_protection): Use asan_redzone_buffer class that is responsible for proper aligned stores and flushing of shadow memory payload. * asan.h (ASAN_MIN_RED_ZONE_SIZE): New. (asan_var_and_redzone_size): Likewise. * cfgexpand.c (expand_stack_vars): Use smaller alignment (ASAN_MIN_RED_ZONE_SIZE) in order to make shadow memory for automatic variables more compact. 2018-11-30 Martin Liska PR sanitizer/81715 * c-c++-common/asan/asan-stack-small.c: New test. (backported 57e4ba18100) Change-Id: I58d73faf84206c26d8459b265e98c97cc242ea4a git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@266664 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 19 ++ gcc/asan.c | 202 ++++++++++++++++----- gcc/asan.h | 25 +++ gcc/cfgexpand.c | 14 +- gcc/testsuite/ChangeLog | 5 + gcc/testsuite/c-c++-common/asan/asan-stack-small.c | 28 +++ 6 files changed, 243 insertions(+), 50 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/asan/asan-stack-small.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 4c738e3..6513b90 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,22 @@ +2018-11-30 Martin Liska + + PR sanitizer/81715 + * asan.c (asan_shadow_cst): Remove, partially transform + into flush_redzone_payload. + (RZ_BUFFER_SIZE): New. + (struct asan_redzone_buffer): New. + (asan_redzone_buffer::emit_redzone_byte): Likewise. + (asan_redzone_buffer::flush_redzone_payload): Likewise. + (asan_redzone_buffer::flush_if_full): Likewise. + (asan_emit_stack_protection): Use asan_redzone_buffer class + that is responsible for proper aligned stores and flushing + of shadow memory payload. + * asan.h (ASAN_MIN_RED_ZONE_SIZE): New. + (asan_var_and_redzone_size): Likewise. + * cfgexpand.c (expand_stack_vars): Use smaller alignment + (ASAN_MIN_RED_ZONE_SIZE) in order to make shadow memory + for automatic variables more compact. + 2018-01-09 Martin Liska PR sanitizer/82517 diff --git a/gcc/asan.c b/gcc/asan.c index 11c2b52..793da99 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -973,20 +973,6 @@ asan_pp_string (pretty_printer *pp) return build1 (ADDR_EXPR, shadow_ptr_types[0], ret); } -/* Return a CONST_INT representing 4 subsequent shadow memory bytes. */ - -static rtx -asan_shadow_cst (unsigned char shadow_bytes[4]) -{ - int i; - unsigned HOST_WIDE_INT val = 0; - gcc_assert (WORDS_BIG_ENDIAN == BYTES_BIG_ENDIAN); - for (i = 0; i < 4; i++) - val |= (unsigned HOST_WIDE_INT) shadow_bytes[BYTES_BIG_ENDIAN ? 3 - i : i] - << (BITS_PER_UNIT * i); - return gen_int_mode (val, SImode); -} - /* Clear shadow memory at SHADOW_MEM, LEN bytes. Can't call a library call here though. */ @@ -1051,6 +1037,136 @@ shadow_mem_size (unsigned HOST_WIDE_INT size) return ROUND_UP (size, ASAN_SHADOW_GRANULARITY) / ASAN_SHADOW_GRANULARITY; } +/* Always emit 4 bytes at a time. */ +#define RZ_BUFFER_SIZE 4 + +/* ASAN redzone buffer container that handles emission of shadow bytes. */ +struct asan_redzone_buffer +{ + /* Constructor. */ + asan_redzone_buffer (rtx shadow_mem, HOST_WIDE_INT prev_offset): + m_shadow_mem (shadow_mem), m_prev_offset (prev_offset), + m_original_offset (prev_offset), m_shadow_bytes (RZ_BUFFER_SIZE) + {} + + /* Emit VALUE shadow byte at a given OFFSET. */ + void emit_redzone_byte (HOST_WIDE_INT offset, unsigned char value); + + /* Emit RTX emission of the content of the buffer. */ + void flush_redzone_payload (void); + +private: + /* Flush if the content of the buffer is full + (equal to RZ_BUFFER_SIZE). */ + void flush_if_full (void); + + /* Memory where we last emitted a redzone payload. */ + rtx m_shadow_mem; + + /* Relative offset where we last emitted a redzone payload. */ + HOST_WIDE_INT m_prev_offset; + + /* Relative original offset. Used for checking only. */ + HOST_WIDE_INT m_original_offset; + +public: + /* Buffer with redzone payload. */ + auto_vec m_shadow_bytes; +}; + +/* Emit VALUE shadow byte at a given OFFSET. */ + +void +asan_redzone_buffer::emit_redzone_byte (HOST_WIDE_INT offset, + unsigned char value) +{ + gcc_assert ((offset & (ASAN_SHADOW_GRANULARITY - 1)) == 0); + gcc_assert (offset >= m_prev_offset); + + HOST_WIDE_INT off + = m_prev_offset + ASAN_SHADOW_GRANULARITY * m_shadow_bytes.length (); + if (off == offset) + { + /* Consecutive shadow memory byte. */ + m_shadow_bytes.safe_push (value); + flush_if_full (); + } + else + { + if (!m_shadow_bytes.is_empty ()) + flush_redzone_payload (); + + /* Maybe start earlier in order to use aligned store. */ + HOST_WIDE_INT align = (offset - m_prev_offset) % ASAN_RED_ZONE_SIZE; + if (align) + { + offset -= align; + for (unsigned i = 0; i < align / BITS_PER_UNIT; i++) + m_shadow_bytes.safe_push (0); + } + + /* Adjust m_prev_offset and m_shadow_mem. */ + HOST_WIDE_INT diff = offset - m_prev_offset; + m_shadow_mem = adjust_address (m_shadow_mem, VOIDmode, + diff >> ASAN_SHADOW_SHIFT); + m_prev_offset = offset; + m_shadow_bytes.safe_push (value); + flush_if_full (); + } +} + +/* Emit RTX emission of the content of the buffer. */ + +void +asan_redzone_buffer::flush_redzone_payload (void) +{ + gcc_assert (WORDS_BIG_ENDIAN == BYTES_BIG_ENDIAN); + + if (m_shadow_bytes.is_empty ()) + return; + + /* Be sure we always emit to an aligned address. */ + gcc_assert (((m_prev_offset - m_original_offset) + & (ASAN_RED_ZONE_SIZE - 1)) == 0); + + /* Fill it to RZ_BUFFER_SIZE bytes with zeros if needed. */ + unsigned l = m_shadow_bytes.length (); + for (unsigned i = 0; i <= RZ_BUFFER_SIZE - l; i++) + m_shadow_bytes.safe_push (0); + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Flushing rzbuffer at offset %" PRId64 " with: ", m_prev_offset); + + unsigned HOST_WIDE_INT val = 0; + for (unsigned i = 0; i < RZ_BUFFER_SIZE; i++) + { + unsigned char v + = m_shadow_bytes[BYTES_BIG_ENDIAN ? RZ_BUFFER_SIZE - i : i]; + val |= (unsigned HOST_WIDE_INT)v << (BITS_PER_UNIT * i); + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "%02x ", v); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\n"); + + rtx c = gen_int_mode (val, SImode); + m_shadow_mem = adjust_address (m_shadow_mem, SImode, 0); + emit_move_insn (m_shadow_mem, c); + m_shadow_bytes.truncate (0); +} + +/* Flush if the content of the buffer is full + (equal to RZ_BUFFER_SIZE). */ + +void +asan_redzone_buffer::flush_if_full (void) +{ + if (m_shadow_bytes.length () == RZ_BUFFER_SIZE) + flush_redzone_payload (); +} + /* Insert code to protect stack vars. The prologue sequence should be emitted directly, epilogue sequence returned. BASE is the register holding the stack base, against which OFFSETS array offsets are relative to, OFFSETS @@ -1072,7 +1188,6 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, rtx_code_label *lab; rtx_insn *insns; char buf[30]; - unsigned char shadow_bytes[4]; 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; @@ -1237,46 +1352,43 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, if (STRICT_ALIGNMENT) set_mem_align (shadow_mem, (GET_MODE_ALIGNMENT (SImode))); prev_offset = base_offset; + + asan_redzone_buffer rz_buffer (shadow_mem, prev_offset); for (l = length; l; l -= 2) { if (l == 2) cur_shadow_byte = ASAN_STACK_MAGIC_RIGHT; offset = offsets[l - 1]; - if ((offset - base_offset) & (ASAN_RED_ZONE_SIZE - 1)) + + bool extra_byte = (offset - base_offset) & (ASAN_SHADOW_GRANULARITY - 1); + /* If a red-zone is not aligned to ASAN_SHADOW_GRANULARITY then + the previous stack variable has size % ASAN_SHADOW_GRANULARITY != 0. + In that case we have to emit one extra byte that will describe + how many bytes (our of ASAN_SHADOW_GRANULARITY) can be accessed. */ + if (extra_byte) { - int i; HOST_WIDE_INT aoff = base_offset + ((offset - base_offset) - & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); - shadow_mem = adjust_address (shadow_mem, VOIDmode, - (aoff - prev_offset) - >> ASAN_SHADOW_SHIFT); - prev_offset = aoff; - for (i = 0; i < 4; i++, aoff += ASAN_SHADOW_GRANULARITY) - if (aoff < offset) - { - if (aoff < offset - (HOST_WIDE_INT)ASAN_SHADOW_GRANULARITY + 1) - shadow_bytes[i] = 0; - else - shadow_bytes[i] = offset - aoff; - } - else - shadow_bytes[i] = ASAN_STACK_MAGIC_MIDDLE; - emit_move_insn (shadow_mem, asan_shadow_cst (shadow_bytes)); - offset = aoff; + & ~(ASAN_SHADOW_GRANULARITY - HOST_WIDE_INT_1)); + rz_buffer.emit_redzone_byte (aoff, offset - aoff); + offset = aoff + ASAN_SHADOW_GRANULARITY; } - while (offset <= offsets[l - 2] - ASAN_RED_ZONE_SIZE) + + /* Calculate size of red zone payload. */ + while (offset < offsets[l - 2]) { - shadow_mem = adjust_address (shadow_mem, VOIDmode, - (offset - prev_offset) - >> ASAN_SHADOW_SHIFT); - prev_offset = offset; - memset (shadow_bytes, cur_shadow_byte, 4); - emit_move_insn (shadow_mem, asan_shadow_cst (shadow_bytes)); - offset += ASAN_RED_ZONE_SIZE; + rz_buffer.emit_redzone_byte (offset, cur_shadow_byte); + offset += ASAN_SHADOW_GRANULARITY; } + cur_shadow_byte = ASAN_STACK_MAGIC_MIDDLE; } + + /* As the automatic variables are aligned to + ASAN_RED_ZONE_SIZE / ASAN_SHADOW_GRANULARITY, the buffer should be + flushed here. */ + gcc_assert (rz_buffer.m_shadow_bytes.is_empty ()); + do_pending_stack_adjust (); /* Construct epilogue sequence. */ @@ -1335,7 +1447,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, for (l = length; l; l -= 2) { offset = base_offset + ((offsets[l - 1] - base_offset) - & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); + & ~(ASAN_MIN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); if (last_offset + last_size != offset) { shadow_mem = adjust_address (shadow_mem, VOIDmode, @@ -1347,7 +1459,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, last_size = 0; } last_size += base_offset + ((offsets[l - 2] - base_offset) - & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)) + & ~(ASAN_MIN_RED_ZONE_SIZE - HOST_WIDE_INT_1)) - offset; /* Unpoison shadow memory that corresponds to a variable that is @@ -1368,7 +1480,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, "%s (%" PRId64 " B)\n", n, size); } - last_size += size & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1); + last_size += size & ~(ASAN_MIN_RED_ZONE_SIZE - HOST_WIDE_INT_1); } } } diff --git a/gcc/asan.h b/gcc/asan.h index efc517e..511da9a 100644 --- a/gcc/asan.h +++ b/gcc/asan.h @@ -50,6 +50,11 @@ extern hash_set *asan_used_labels; up to 2 * ASAN_RED_ZONE_SIZE - 1 bytes. */ #define ASAN_RED_ZONE_SIZE 32 +/* Stack variable use more compact red zones. The size includes also + size of variable itself. */ + +#define ASAN_MIN_RED_ZONE_SIZE 16 + /* Shadow memory values for stack protection. Left is below protected vars, the first pointer in stack corresponding to that offset contains ASAN_STACK_FRAME_MAGIC word, the second pointer to a string describing @@ -97,6 +102,26 @@ asan_red_zone_size (unsigned int size) return c ? 2 * ASAN_RED_ZONE_SIZE - c : ASAN_RED_ZONE_SIZE; } +/* Return how much a stack variable occupis on a stack + including a space for red zone. */ + +static inline unsigned HOST_WIDE_INT +asan_var_and_redzone_size (unsigned HOST_WIDE_INT size) +{ + if (size <= 4) + return 16; + else if (size <= 16) + return 32; + else if (size <= 128) + return size + 32; + else if (size <= 512) + return size + 64; + else if (size <= 4096) + return size + 128; + else + return size + 256; +} + extern bool set_asan_shadow_offset (const char *); extern void set_sanitized_sections (const char *); diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 475e2f6..2a541b8 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -1126,13 +1126,17 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data) { HOST_WIDE_INT prev_offset = align_base (frame_offset, - MAX (alignb, ASAN_RED_ZONE_SIZE), + MAX (alignb, ASAN_MIN_RED_ZONE_SIZE), !FRAME_GROWS_DOWNWARD); tree repr_decl = NULL_TREE; - offset - = alloc_stack_frame_space (stack_vars[i].size - + ASAN_RED_ZONE_SIZE, - MAX (alignb, ASAN_RED_ZONE_SIZE)); + unsigned HOST_WIDE_INT size + = asan_var_and_redzone_size (stack_vars[i].size); + if (data->asan_vec.is_empty ()) + size = MAX (size, ASAN_RED_ZONE_SIZE); + + unsigned HOST_WIDE_INT alignment = MAX (alignb, + ASAN_MIN_RED_ZONE_SIZE); + offset = alloc_stack_frame_space (size, alignment); data->asan_vec.safe_push (prev_offset); data->asan_vec.safe_push (offset + stack_vars[i].size); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index e5bdc89..1e59222 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2018-11-30 Martin Liska + + PR sanitizer/81715 + * c-c++-common/asan/asan-stack-small.c: New test. + 2018-01-01 Jakub Jelinek PR middle-end/83623 diff --git a/gcc/testsuite/c-c++-common/asan/asan-stack-small.c b/gcc/testsuite/c-c++-common/asan/asan-stack-small.c new file mode 100644 index 0000000..11a56b8 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/asan-stack-small.c @@ -0,0 +1,28 @@ +/* { dg-do run } */ + +char *pa; +char *pb; +char *pc; + +void access (volatile char *ptr) +{ + *ptr = 'x'; +} + +int main (int argc, char **argv) +{ + char a; + char b; + char c; + + pa = &a; + pb = &b; + pc = &c; + + access (pb); + access (pc); + // access 'b' here + access (pa + 32); + + return 0; +} -- 2.7.4 From 7810f4013db4b6ec8108a4a1c66a1ec2d8a9f377 Mon Sep 17 00:00:00 2001 From: jakub Date: Sat, 1 Dec 2018 07:31:56 +0000 Subject: [PATCH 09/16] PR sanitizer/88289 * asan.c (asan_redzone_buffer::flush_redzone_payload): Fix up an off-by-one for BYTES_BIG_ENDIAN. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@266708 138bc75d-0d04-0410-961f-82ee72b054a4 (cherry picked ef0c2db0d6a9d324bca1f4d2d4478034a63d54a7) Change-Id: Ied661ed605dc4d060c78e5c6db31763fd35e77e1 --- gcc/ChangeLog | 5 +++++ gcc/asan.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6513b90..2dd219f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2018-12-01 Jakub Jelinek + PR sanitizer/88289 + * asan.c (asan_redzone_buffer::flush_redzone_payload): Fix up + an off-by-one for BYTES_BIG_ENDIAN. + 2018-11-30 Martin Liska PR sanitizer/81715 diff --git a/gcc/asan.c b/gcc/asan.c index 793da99..3a20723 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -1142,7 +1142,7 @@ asan_redzone_buffer::flush_redzone_payload (void) for (unsigned i = 0; i < RZ_BUFFER_SIZE; i++) { unsigned char v - = m_shadow_bytes[BYTES_BIG_ENDIAN ? RZ_BUFFER_SIZE - i : i]; + = m_shadow_bytes[BYTES_BIG_ENDIAN ? RZ_BUFFER_SIZE - i - 1 : i]; val |= (unsigned HOST_WIDE_INT)v << (BITS_PER_UNIT * i); if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "%02x ", v); -- 2.7.4 From d4a907412da42b5a3d9adb4bb248c8e03d1bec38 Mon Sep 17 00:00:00 2001 From: jakub Date: Sun, 2 Dec 2018 12:39:26 +0000 Subject: [PATCH 10/16] PR sanitizer/88291 * asan.c (asan_clear_shadow): Move assert that len is multiple of 4 to the start of the function. (asan_emit_stack_protection): When emitting clearing sequence for epilogue, align offset down to ASAN_RED_ZONE_SIZE granularity, add last_size_aligned which is last_size padded to multiples of ASAN_RED_ZONE_SIZE and emit asan_clear_shadow always on 4 byte boundaries. * c-c++-common/asan/pr88291.c: New test. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@266721 138bc75d-0d04-0410-961f-82ee72b054a4 (cherry picked b0d945ba743397d845ab6bc3c3834241f28bb91f) Change-Id: I8a86e9d61617253e8439ca2c5659a2436604d30b --- gcc/ChangeLog | 11 +++++++++++ gcc/asan.c | 20 +++++++++++++------- gcc/testsuite/ChangeLog | 11 +++++++++++ gcc/testsuite/c-c++-common/asan/pr88291.c | 14 ++++++++++++++ 4 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/asan/pr88291.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2dd219f..8c0d502 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +2018-12-02 Jakub Jelinek + + PR sanitizer/88291 + * asan.c (asan_clear_shadow): Move assert that len is multiple of 4 + to the start of the function. + (asan_emit_stack_protection): When emitting clearing sequence for + epilogue, align offset down to ASAN_RED_ZONE_SIZE granularity, + add last_size_aligned which is last_size padded to multiples of + ASAN_RED_ZONE_SIZE and emit asan_clear_shadow always on 4 byte + boundaries. + 2018-12-01 Jakub Jelinek PR sanitizer/88289 * asan.c (asan_redzone_buffer::flush_redzone_payload): Fix up diff --git a/gcc/asan.c b/gcc/asan.c index 3a20723..813e97b 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -983,6 +983,7 @@ asan_clear_shadow (rtx shadow_mem, HOST_WIDE_INT len) rtx_code_label *top_label; rtx end, addr, tmp; + gcc_assert ((len & 3) == 0); start_sequence (); clear_storage (shadow_mem, GEN_INT (len), BLOCK_OP_NORMAL); insns = get_insns (); @@ -996,7 +997,6 @@ asan_clear_shadow (rtx shadow_mem, HOST_WIDE_INT len) return; } - gcc_assert ((len & 3) == 0); top_label = gen_label_rtx (); addr = copy_to_mode_reg (Pmode, XEXP (shadow_mem, 0)); shadow_mem = adjust_automodify_address (shadow_mem, SImode, addr, 0); @@ -1191,7 +1191,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, last_size; + HOST_WIDE_INT last_offset, last_size, last_size_aligned; int l; unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT; tree str_cst, decl, id; @@ -1444,20 +1444,23 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, prev_offset = base_offset; last_offset = base_offset; last_size = 0; + last_size_aligned = 0; for (l = length; l; l -= 2) { offset = base_offset + ((offsets[l - 1] - base_offset) - & ~(ASAN_MIN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); - if (last_offset + last_size != offset) + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); + if (last_offset + last_size_aligned < offset) { 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); + asan_clear_shadow (shadow_mem, last_size_aligned >> ASAN_SHADOW_SHIFT); last_offset = offset; last_size = 0; } + else + last_size = offset - last_offset; last_size += base_offset + ((offsets[l - 2] - base_offset) & ~(ASAN_MIN_RED_ZONE_SIZE - HOST_WIDE_INT_1)) - offset; @@ -1483,13 +1486,16 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, last_size += size & ~(ASAN_MIN_RED_ZONE_SIZE - HOST_WIDE_INT_1); } } + last_size_aligned + = ((last_size + (ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)) + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); } - if (last_size) + if (last_size_aligned) { shadow_mem = adjust_address (shadow_mem, VOIDmode, (last_offset - prev_offset) >> ASAN_SHADOW_SHIFT); - asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT); + asan_clear_shadow (shadow_mem, last_size_aligned >> ASAN_SHADOW_SHIFT); } /* Clean-up set with instrumented stack variables. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 1e59222..2e6c35b 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2018-12-02 Jakub Jelinek + + PR sanitizer/88291 + * asan.c (asan_clear_shadow): Move assert that len is multiple of 4 + to the start of the function. + (asan_emit_stack_protection): When emitting clearing sequence for + epilogue, align offset down to ASAN_RED_ZONE_SIZE granularity, + add last_size_aligned which is last_size padded to multiples of + ASAN_RED_ZONE_SIZE and emit asan_clear_shadow always on 4 byte + boundaries. + 2018-11-30 Martin Liska PR sanitizer/81715 diff --git a/gcc/testsuite/c-c++-common/asan/pr88291.c b/gcc/testsuite/c-c++-common/asan/pr88291.c new file mode 100644 index 0000000..e865266 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/pr88291.c @@ -0,0 +1,14 @@ +/* PR sanitizer/88291 */ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=address -Os" } */ +/* { dg-additional-options "-mstringop-strategy=libcall" { target i?86-*-* x86_64-*-* } } */ + +void bar (void *, void *); + +void +foo (void) +{ + int b; + char __attribute__((aligned(16))) a[(1 << 20) + 1]; + bar (&a, &b); +} -- 2.7.4 From 73efd936dfa88bcabb3434623b0e5bf361951d08 Mon Sep 17 00:00:00 2001 From: jakub Date: Wed, 5 Dec 2018 08:26:06 +0000 Subject: [PATCH 11/16] PR sanitizer/88333 * cfgexpand.c (expand_stack_vars): If asan_vec is empty, start with aligning frame offset to ASAN_RED_ZONE_SIZE bytes. * c-c++-common/asan/pr88333.c: New test. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@266817 138bc75d-0d04-0410-961f-82ee72b054a4 (backported db93a9789e3993801a493fcde11da7f08f212f6e) Change-Id: Ie0c2c8f000777190e316c4051e2e781a5be823ef --- gcc/ChangeLog | 6 ++++++ gcc/cfgexpand.c | 5 +++++ gcc/testsuite/ChangeLog | 5 +++++ gcc/testsuite/c-c++-common/asan/pr88333.c | 12 ++++++++++++ 4 files changed, 28 insertions(+) create mode 100644 gcc/testsuite/c-c++-common/asan/pr88333.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 8c0d502..6c74cb2 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2018-12-05 Jakub Jelinek + + PR sanitizer/88333 + * cfgexpand.c (expand_stack_vars): If asan_vec is empty, start with + aligning frame offset to ASAN_RED_ZONE_SIZE bytes. + 2018-12-02 Jakub Jelinek PR sanitizer/88291 diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 2a541b8..1c4736a 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -1128,6 +1128,11 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data) = align_base (frame_offset, MAX (alignb, ASAN_MIN_RED_ZONE_SIZE), !FRAME_GROWS_DOWNWARD); + if (data->asan_vec.is_empty ()) + { + alloc_stack_frame_space (0, ASAN_RED_ZONE_SIZE); + prev_offset = frame_offset; + } tree repr_decl = NULL_TREE; unsigned HOST_WIDE_INT size = asan_var_and_redzone_size (stack_vars[i].size); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 2e6c35b..7dc8ba3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2018-12-05 Jakub Jelinek + + PR sanitizer/88333 + * c-c++-common/asan/pr88333.c: New test. + 2018-12-02 Jakub Jelinek PR sanitizer/88291 diff --git a/gcc/testsuite/c-c++-common/asan/pr88333.c b/gcc/testsuite/c-c++-common/asan/pr88333.c new file mode 100644 index 0000000..0f257a8 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/pr88333.c @@ -0,0 +1,12 @@ +/* PR sanitizer/88333 */ +/* { dg-do compile { target fstack_protector } } */ +/* { dg-options "-fstack-protector-strong -fsanitize=address" } */ + +void bar (int *); + +void +foo (void) +{ + int c; + bar (&c); +} -- 2.7.4 From 9c821914467fa884a0b3b57fe2fa93a5dc71136b Mon Sep 17 00:00:00 2001 From: ebotcazou Date: Tue, 12 Feb 2019 12:23:08 +0000 Subject: [PATCH 12/16] * asan.c (asan_expand_mark_ifn): Take into account the alignment of the object to pick the size of stores on strict-alignment platforms. * config/sparc/sparc.md (*movsi_insn): Minor tweak. (*movdi_insn_sp32): Likewise. (*movdi_insn_sp64): Likewise. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@268792 138bc75d-0d04-0410-961f-82ee72b054a4 (backported 3f00a89f9cdb0f2838ce0d853ef3bf6ff6f5a3ad without config/sparc fixes) Change-Id: Id53952131b13fe5eba6b6de1f6d40531833d19fd --- gcc/ChangeLog | 9 +++++++++ gcc/asan.c | 11 ++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6c74cb2..ffa65a3 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +2019-02-12 Eric Botcazou + + * asan.c (asan_expand_mark_ifn): Take into account the alignment of + the object to pick the size of stores on strict-alignment platforms. + + * config/sparc/sparc.md (*movsi_insn): Minor tweak. + (*movdi_insn_sp32): Likewise. + (*movdi_insn_sp64): Likewise. + 2018-12-05 Jakub Jelinek PR sanitizer/88333 diff --git a/gcc/asan.c b/gcc/asan.c index 813e97b..b32dbc9 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -2881,7 +2881,10 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter) /* Generate direct emission if size_in_bytes is small. */ if (size_in_bytes <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD) { - unsigned HOST_WIDE_INT shadow_size = shadow_mem_size (size_in_bytes); + const unsigned HOST_WIDE_INT shadow_size + = shadow_mem_size (size_in_bytes); + const unsigned int shadow_align + = (get_pointer_alignment (base) / BITS_PER_UNIT) >> ASAN_SHADOW_SHIFT; tree shadow = build_shadow_mem_access (iter, loc, base_addr, shadow_ptr_types[0], true); @@ -2889,9 +2892,11 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter) for (unsigned HOST_WIDE_INT offset = 0; offset < shadow_size;) { unsigned size = 1; - if (shadow_size - offset >= 4) + if (shadow_size - offset >= 4 + && (!STRICT_ALIGNMENT || shadow_align >= 4)) size = 4; - else if (shadow_size - offset >= 2) + else if (shadow_size - offset >= 2 + && (!STRICT_ALIGNMENT || shadow_align >= 2)) size = 2; unsigned HOST_WIDE_INT last_chunk_size = 0; -- 2.7.4 From 1f34038093e95f5e302bf8f010e3efec921c69d5 Mon Sep 17 00:00:00 2001 From: ebotcazou Date: Fri, 15 Feb 2019 21:40:24 +0000 Subject: [PATCH 13/16] * asan.c (asan_emit_stack_protection): Use full-sized mask to align the base address on 64-bit strict-alignment platforms. (cherry picked from 724165bd22886d1fa486513c1b695302b307726e) Change-Id: I52f5266e8336b7959b655226ae8619afc35b2692 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@268949 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 5 +++++ gcc/asan.c | 12 +++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index ffa65a3..ecad014 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2019-02-15 Eric Botcazou + + * asan.c (asan_emit_stack_protection): Use full-sized mask to align + the base address on 64-bit strict-alignment platforms. + 2019-02-12 Eric Botcazou * asan.c (asan_expand_mark_ifn): Take into account the alignment of diff --git a/gcc/asan.c b/gcc/asan.c index b32dbc9..ae83c69 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -1256,13 +1256,15 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, base_align_bias = ((asan_frame_size + alignb - 1) & ~(alignb - HOST_WIDE_INT_1)) - asan_frame_size; } + /* Align base if target is STRICT_ALIGNMENT. */ if (STRICT_ALIGNMENT) - base = expand_binop (Pmode, and_optab, base, - gen_int_mode (-((GET_MODE_ALIGNMENT (SImode) - << ASAN_SHADOW_SHIFT) - / BITS_PER_UNIT), Pmode), NULL_RTX, - 1, OPTAB_DIRECT); + { + const HOST_WIDE_INT align + = (GET_MODE_ALIGNMENT (SImode) / BITS_PER_UNIT) << ASAN_SHADOW_SHIFT; + base = expand_binop (Pmode, and_optab, base, gen_int_mode (-align, Pmode), + NULL_RTX, 1, OPTAB_DIRECT); + } if (use_after_return_class == -1 && pbase) emit_move_insn (pbase, base); -- 2.7.4 From d0abe09c4a743e0176d949ca4e10186369b51498 Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov Date: Fri, 28 Jun 2019 21:31:32 +0300 Subject: [PATCH 14/16] Enable build-tunable asan shadow scale size Use the following for the project config (3 is default): %define asan_shadow_scale 3 Macros: asan_shadow_scale 3 :Macros + backport https://reviews.llvm.org/D39471 for x86_64 shadow offset + backport https://reviews.llvm.org/D39472 for minimum redzone size + backport https://reviews.llvm.org/D39473 for internal alloc min alignment + backport https://reviews.llvm.org/D39474 to avoid assert failure for non-default shadow scale Change-Id: I23b231f03d6ab47c6b4264f8c7837eb1197f4cc4 --- gcc/asan.h | 4 ++++ gcc/config/i386/i386.c | 15 ++++++++++++--- libsanitizer/asan/asan_activation.cc | 7 +++++-- libsanitizer/asan/asan_fake_stack.cc | 4 ++-- libsanitizer/asan/asan_flags.cc | 4 ++++ libsanitizer/asan/asan_mapping.h | 7 ++++++- libsanitizer/asan/asan_rtl.cc | 1 + libsanitizer/sanitizer_common/sanitizer_allocator.cc | 9 ++++++++- libsanitizer/sanitizer_common/sanitizer_common.h | 2 ++ packaging/gcc-aarch64.spec | 1 + packaging/gcc-armv7hl.spec | 1 + packaging/gcc-armv7l.spec | 1 + packaging/linaro-gcc.spec | 1 + 13 files changed, 48 insertions(+), 9 deletions(-) diff --git a/gcc/asan.h b/gcc/asan.h index 511da9a..66f7e90 100644 --- a/gcc/asan.h +++ b/gcc/asan.h @@ -43,7 +43,11 @@ extern hash_set *asan_used_labels; /* Shadow memory is found at (address >> ASAN_SHADOW_SHIFT) + asan_shadow_offset (). */ +#if defined(ASAN_SHADOW_SCALE) +#define ASAN_SHADOW_SHIFT ASAN_SHADOW_SCALE +#else #define ASAN_SHADOW_SHIFT 3 +#endif #define ASAN_SHADOW_GRANULARITY (1UL << ASAN_SHADOW_SHIFT) /* Red zone size, stack and global variables are padded by ASAN_RED_ZONE_SIZE diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 898c247..bd043c4 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -79,6 +79,7 @@ along with GCC; see the file COPYING3. If not see #include "dojump.h" #include "fold-const-call.h" #include "tree-ssanames.h" +#include "asan.h" /* This file should be included last. */ #include "target-def.h" @@ -7374,12 +7375,20 @@ ix86_legitimate_combined_insn (rtx_insn *insn) /* Implement the TARGET_ASAN_SHADOW_OFFSET hook. */ +#if !defined(TARGET_MACHO) && !defined(ASAN_SHADOW_SHIFT) +# error ASAN_SHADOW_SHIFT is not defined +#endif + +static const unsigned HOST_WIDE_INT asan_short_64bit_shadow_offset = + 0x7FFFFFFF & (~0xFFFULL << ASAN_SHADOW_SHIFT); + static unsigned HOST_WIDE_INT ix86_asan_shadow_offset (void) { - return TARGET_LP64 ? (TARGET_MACHO ? (HOST_WIDE_INT_1 << 44) - : HOST_WIDE_INT_C (0x7fff8000)) - : (HOST_WIDE_INT_1 << 29); + return TARGET_LP64 + ? (TARGET_MACHO ? (HOST_WIDE_INT_1 << 44) + : asan_short_64bit_shadow_offset) + : (HOST_WIDE_INT_1 << 29); } /* Argument support functions. */ diff --git a/libsanitizer/asan/asan_activation.cc b/libsanitizer/asan/asan_activation.cc index 26798e7..0c0ae09 100644 --- a/libsanitizer/asan/asan_activation.cc +++ b/libsanitizer/asan/asan_activation.cc @@ -14,8 +14,10 @@ #include "asan_allocator.h" #include "asan_flags.h" #include "asan_internal.h" +#include "asan_mapping.h" #include "asan_poisoning.h" #include "asan_stack.h" +#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" namespace __asan { @@ -107,8 +109,9 @@ void AsanDeactivate() { AllocatorOptions disabled = asan_deactivated_flags.allocator_options; disabled.quarantine_size_mb = 0; - disabled.min_redzone = 16; // Redzone must be at least 16 bytes long. - disabled.max_redzone = 16; + // Redzone must be at least Max(16, granularity) bytes long. + disabled.min_redzone = Max(16, (int)SHADOW_GRANULARITY); + disabled.max_redzone = disabled.min_redzone; disabled.alloc_dealloc_mismatch = false; disabled.may_return_null = true; ReInitializeAllocator(disabled); diff --git a/libsanitizer/asan/asan_fake_stack.cc b/libsanitizer/asan/asan_fake_stack.cc index bf7566a..a2e3f4e 100644 --- a/libsanitizer/asan/asan_fake_stack.cc +++ b/libsanitizer/asan/asan_fake_stack.cc @@ -26,9 +26,9 @@ static const u64 kAllocaRedzoneMask = 31UL; // For small size classes inline PoisonShadow for better performance. ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) { - CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3. u64 *shadow = reinterpret_cast(MemToShadow(ptr)); - if (class_id <= 6) { + if (SHADOW_SCALE == 3 && class_id <= 6) { + // This code expects SHADOW_SCALE=3. for (uptr i = 0; i < (((uptr)1) << class_id); i++) { shadow[i] = magic; // Make sure this does not become memset. diff --git a/libsanitizer/asan/asan_flags.cc b/libsanitizer/asan/asan_flags.cc index 39473bb..6639841 100644 --- a/libsanitizer/asan/asan_flags.cc +++ b/libsanitizer/asan/asan_flags.cc @@ -13,6 +13,7 @@ #include "asan_activation.h" #include "asan_flags.h" #include "asan_interface_internal.h" +#include "asan_mapping.h" #include "asan_stack.h" #include "lsan/lsan_common.h" #include "sanitizer_common/sanitizer_common.h" @@ -133,6 +134,9 @@ void InitializeFlags() { SanitizerToolName); Die(); } + // Ensure that redzone is at least SHADOW_GRANULARITY. + if (f->redzone < (int)SHADOW_GRANULARITY) + f->redzone = SHADOW_GRANULARITY; // Make "strict_init_order" imply "check_initialization_order". // TODO(samsonov): Use a single runtime flag for an init-order checker. if (f->strict_init_order) { diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h index b9fa5f7..040cde6 100644 --- a/libsanitizer/asan/asan_mapping.h +++ b/libsanitizer/asan/asan_mapping.h @@ -122,11 +122,16 @@ // || `[0x30000000, 0x35ffffff]` || LowShadow || // || `[0x00000000, 0x2fffffff]` || LowMem || +#if defined(ASAN_SHADOW_SCALE) +static const u64 kDefaultShadowScale = ASAN_SHADOW_SCALE; +#else static const u64 kDefaultShadowScale = 3; +#endif static const u64 kDefaultShadowSentinel = ~(uptr)0; static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000 static const u64 kDefaultShadowOffset64 = 1ULL << 44; -static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G. +static const u64 kDefaultShort64bitShadowOffset = + 0x7FFFFFFF & (~0xFFFULL << kDefaultShadowScale); // < 2G. static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000 static const u64 kIosShadowOffset64 = 0x120200000; static const u64 kIosSimShadowOffset32 = 1ULL << 30; diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc index 5bafb9a..9ff2900 100644 --- a/libsanitizer/asan/asan_rtl.cc +++ b/libsanitizer/asan/asan_rtl.cc @@ -469,6 +469,7 @@ static void AsanInitInternal() { MaybeReexec(); // Setup internal allocator callback. + SetLowLevelAllocateMinAlignment(SHADOW_GRANULARITY); SetLowLevelAllocateCallback(OnLowLevelAllocate); InitializeAsanInterceptors(); diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.cc b/libsanitizer/sanitizer_common/sanitizer_allocator.cc index 5af4fba..d68c207 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.cc +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.cc @@ -175,11 +175,13 @@ void InternalFree(void *addr, InternalAllocatorCache *cache) { } // LowLevelAllocator +constexpr uptr kLowLevelAllocatorDefaultAlignment = 8; +static uptr low_level_alloc_min_alignment = kLowLevelAllocatorDefaultAlignment; static LowLevelAllocateCallback low_level_alloc_callback; void *LowLevelAllocator::Allocate(uptr size) { // Align allocation size. - size = RoundUpTo(size, 8); + size = RoundUpTo(size, low_level_alloc_min_alignment); if (allocated_end_ - allocated_current_ < (sptr)size) { uptr size_to_allocate = Max(size, GetPageSizeCached()); allocated_current_ = @@ -196,6 +198,11 @@ void *LowLevelAllocator::Allocate(uptr size) { return res; } +void SetLowLevelAllocateMinAlignment(uptr alignment) { + CHECK(IsPowerOfTwo(alignment)); + low_level_alloc_min_alignment = Max(alignment, low_level_alloc_min_alignment); +} + void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) { low_level_alloc_callback = callback; } diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h index fb06aa4..79017ca 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.h +++ b/libsanitizer/sanitizer_common/sanitizer_common.h @@ -174,6 +174,8 @@ class LowLevelAllocator { char *allocated_end_; char *allocated_current_; }; +// Set the min alignment of LowLevelAllocator to at least alignment. +void SetLowLevelAllocateMinAlignment(uptr alignment); typedef void (*LowLevelAllocateCallback)(uptr ptr, uptr size); // Allows to register tool-specific callbacks for LowLevelAllocator. // Passing NULL removes the callback. diff --git a/packaging/gcc-aarch64.spec b/packaging/gcc-aarch64.spec index 52a7c9d..25eaa94 100644 --- a/packaging/gcc-aarch64.spec +++ b/packaging/gcc-aarch64.spec @@ -727,6 +727,7 @@ if [ ! -z "$(echo $RPM_OPT_FLAGS | grep -o "\B\-Wformat\-")" ]; then fi %{?asan: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -DASAN_INIT_FIRST) } +%{?asan_shadow_scale: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -Wp,-DASAN_SHADOW_SCALE=%{asan_shadow_scale})} %ifarch armv7l armv7hl aarch64 %undefine gcc_profiledbootstrap diff --git a/packaging/gcc-armv7hl.spec b/packaging/gcc-armv7hl.spec index 3540bfe..b5a36ff 100644 --- a/packaging/gcc-armv7hl.spec +++ b/packaging/gcc-armv7hl.spec @@ -727,6 +727,7 @@ if [ ! -z "$(echo $RPM_OPT_FLAGS | grep -o "\B\-Wformat\-")" ]; then fi %{?asan: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -DASAN_INIT_FIRST) } +%{?asan_shadow_scale: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -Wp,-DASAN_SHADOW_SCALE=%{asan_shadow_scale})} %ifarch armv7l armv7hl aarch64 %undefine gcc_profiledbootstrap diff --git a/packaging/gcc-armv7l.spec b/packaging/gcc-armv7l.spec index f9a6241..fda85b8 100644 --- a/packaging/gcc-armv7l.spec +++ b/packaging/gcc-armv7l.spec @@ -727,6 +727,7 @@ if [ ! -z "$(echo $RPM_OPT_FLAGS | grep -o "\B\-Wformat\-")" ]; then fi %{?asan: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -DASAN_INIT_FIRST) } +%{?asan_shadow_scale: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -Wp,-DASAN_SHADOW_SCALE=%{asan_shadow_scale})} %ifarch armv7l armv7hl aarch64 %undefine gcc_profiledbootstrap diff --git a/packaging/linaro-gcc.spec b/packaging/linaro-gcc.spec index 52a1b5f..fde4e38 100644 --- a/packaging/linaro-gcc.spec +++ b/packaging/linaro-gcc.spec @@ -724,6 +724,7 @@ if [ ! -z "$(echo $RPM_OPT_FLAGS | grep -o "\B\-Wformat\-")" ]; then fi %{?asan: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -DASAN_INIT_FIRST) } +%{?asan_shadow_scale: RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS -Wp,-DASAN_SHADOW_SCALE=%{asan_shadow_scale})} %ifarch armv7l armv7hl aarch64 %undefine gcc_profiledbootstrap -- 2.7.4 From 14d5c9218c5023638e222d167e35a5d40b04fc5c Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov Date: Fri, 12 Jul 2019 20:55:45 +0300 Subject: [PATCH 15/16] Add support for compiler instrumentation with non-default ASan scale * asan.c (asan_clear_shadow): Adjust for the custom ASAN_SHADOW_SHIFT. (asan_redzone_buffer::emit_redzone_byte): Same. (asan_redzone_buffer): New assert. (asan_emit_stack_protection): New asserts for custom ASAN_SHADOW_SHIFT. (asan_emit_stack_protection): Additional redzone flush for the high shadow granularities. (asan_expand_check_ifn): Adjust for custom ASAN_SHADOW_SHIFT. (asan_expand_check_ifn): Same. * asan.h (ASAN_RED_ZONE_SIZE): Adjust for custom ASAN_SHADOW_SHIFT. (ASAN_MIN_RED_ZONE_SIZE): Same. * cfgexpand.c (expand_used_vars): Remove cast. Change-Id: I770677b502d8a83c765d34a1c5217a947d501693 --- gcc/asan.c | 31 ++++++++++++++++++++++--------- gcc/asan.h | 5 +++-- gcc/cfgexpand.c | 2 +- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/gcc/asan.c b/gcc/asan.c index ae83c69..a2e4ff9 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -983,7 +983,7 @@ asan_clear_shadow (rtx shadow_mem, HOST_WIDE_INT len) rtx_code_label *top_label; rtx end, addr, tmp; - gcc_assert ((len & 3) == 0); + gcc_assert ((len & ((ASAN_RED_ZONE_SIZE >> ASAN_SHADOW_SHIFT) - 1)) == 0); start_sequence (); clear_storage (shadow_mem, GEN_INT (len), BLOCK_OP_NORMAL); insns = get_insns (); @@ -1101,7 +1101,7 @@ asan_redzone_buffer::emit_redzone_byte (HOST_WIDE_INT offset, if (align) { offset -= align; - for (unsigned i = 0; i < align / BITS_PER_UNIT; i++) + for (unsigned i = 0; i < align / ASAN_SHADOW_GRANULARITY; i++) m_shadow_bytes.safe_push (0); } @@ -1131,6 +1131,7 @@ asan_redzone_buffer::flush_redzone_payload (void) /* Fill it to RZ_BUFFER_SIZE bytes with zeros if needed. */ unsigned l = m_shadow_bytes.length (); + gcc_assert (RZ_BUFFER_SIZE >= l); for (unsigned i = 0; i <= RZ_BUFFER_SIZE - l; i++) m_shadow_bytes.safe_push (0); @@ -1347,8 +1348,12 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, = plus_constant (Pmode, shadow_base, asan_shadow_offset () + (base_align_bias >> ASAN_SHADOW_SHIFT)); - gcc_assert (asan_shadow_set != -1 - && (ASAN_RED_ZONE_SIZE >> ASAN_SHADOW_SHIFT) == 4); + gcc_assert (asan_shadow_set != -1); + gcc_assert (ASAN_SHADOW_GRANULARITY >= 8 + && ASAN_SHADOW_GRANULARITY <= 64); + gcc_assert (ASAN_MIN_RED_ZONE_SIZE >= 16 + && ASAN_MIN_RED_ZONE_SIZE >= ASAN_SHADOW_GRANULARITY); + gcc_assert (ASAN_RED_ZONE_SIZE >= 32UL); shadow_mem = gen_rtx_MEM (SImode, shadow_base); set_mem_alias_set (shadow_mem, asan_shadow_set); if (STRICT_ALIGNMENT) @@ -1386,6 +1391,11 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb, cur_shadow_byte = ASAN_STACK_MAGIC_MIDDLE; } + /* Right redzone could be still in buffer for high shadow scales. */ + if (ASAN_SHADOW_SHIFT > 3 + && rz_buffer.m_shadow_bytes.length() < RZ_BUFFER_SIZE) + rz_buffer.flush_redzone_payload(); + /* As the automatic variables are aligned to ASAN_RED_ZONE_SIZE / ASAN_SHADOW_GRANULARITY, the buffer should be flushed here. */ @@ -2985,7 +2995,8 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls) HOST_WIDE_INT real_size_in_bytes = size_in_bytes == -1 ? 1 : size_in_bytes; - tree shadow_ptr_type = shadow_ptr_types[real_size_in_bytes == 16 ? 1 : 0]; + tree shadow_ptr_type = shadow_ptr_types[real_size_in_bytes == + (2 * ASAN_SHADOW_GRANULARITY) ? 1 : 0]; tree shadow_type = TREE_TYPE (shadow_ptr_type); gimple_stmt_iterator gsi = *iter; @@ -3036,7 +3047,7 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls) tree base_addr = gimple_assign_lhs (g); tree t = NULL_TREE; - if (real_size_in_bytes >= 8) + if (real_size_in_bytes >= ASAN_SHADOW_GRANULARITY) { tree shadow = build_shadow_mem_access (&gsi, loc, base_addr, shadow_ptr_type); @@ -3055,10 +3066,11 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls) /* Aligned (>= 8 bytes) can test just (real_size_in_bytes - 1 >= shadow), as base_addr & 7 is known to be 0. */ - if (align < 8) + if (align < ASAN_SHADOW_GRANULARITY) { gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, - base_addr, 7)); + base_addr, + ASAN_SHADOW_GRANULARITY - 1)); gimple_seq_add_stmt (&seq, build_type_cast (shadow_type, gimple_seq_last (seq))); @@ -3100,7 +3112,8 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls) gimple_seq seq = NULL; gimple_seq_add_stmt (&seq, shadow_test); gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, - base_end_addr, 7)); + base_end_addr, + ASAN_SHADOW_GRANULARITY - 1)); gimple_seq_add_stmt (&seq, build_type_cast (shadow_type, gimple_seq_last (seq))); gimple_seq_add_stmt (&seq, build_assign (GE_EXPR, diff --git a/gcc/asan.h b/gcc/asan.h index 66f7e90..333d11c 100644 --- a/gcc/asan.h +++ b/gcc/asan.h @@ -52,12 +52,13 @@ extern hash_set *asan_used_labels; /* Red zone size, stack and global variables are padded by ASAN_RED_ZONE_SIZE up to 2 * ASAN_RED_ZONE_SIZE - 1 bytes. */ -#define ASAN_RED_ZONE_SIZE 32 +#define ASAN_RED_ZONE_SIZE \ + ((ASAN_SHADOW_GRANULARITY < 32UL) ? 32UL : ASAN_SHADOW_GRANULARITY) /* Stack variable use more compact red zones. The size includes also size of variable itself. */ -#define ASAN_MIN_RED_ZONE_SIZE 16 +#define ASAN_MIN_RED_ZONE_SIZE (2 * ASAN_SHADOW_GRANULARITY) /* Shadow memory values for stack protection. Left is below protected vars, the first pointer in stack corresponding to that offset contains diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 1c4736a..48ecb16 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -2215,7 +2215,7 @@ expand_used_vars (void) sz = data.asan_vec[0] - prev_offset; if (data.asan_alignb > ASAN_RED_ZONE_SIZE && data.asan_alignb <= 4096 - && sz + ASAN_RED_ZONE_SIZE >= (int) data.asan_alignb) + && sz + ASAN_RED_ZONE_SIZE >= data.asan_alignb) redzonesz = ((sz + ASAN_RED_ZONE_SIZE + data.asan_alignb - 1) & ~(data.asan_alignb - HOST_WIDE_INT_1)) - sz; offset -- 2.7.4 From 01baebcf2719b6e9f67b91f4856a4875fb2f2852 Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov Date: Thu, 25 Jul 2019 21:14:24 +0300 Subject: [PATCH 16/16] Fix annobin merge unresolved conflicts Change-Id: If4169ae791fa6f3cdc1dec358cfdde80a2ae7b96 --- gcc/annobin.c | 84 +++++++++++++++++++++++------------------------------------ gcc/annobin.h | 2 +- gcc/cp/decl.c | 2 +- 3 files changed, 35 insertions(+), 53 deletions(-) diff --git a/gcc/annobin.c b/gcc/annobin.c index f3d2a4d..a9a14de 100644 --- a/gcc/annobin.c +++ b/gcc/annobin.c @@ -1795,58 +1795,40 @@ annobin_target_specific_loader_notes (char * ptr, size_t size) } void -annobin_search_abi_declaration (tree decl) +annobin_search_abi_declaration (tree decl, const char *name) { 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)) { + + if (!decl || !name) + { + annobin_inform(1, "Can't get the variable identifier"); 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; - } - } - } + if (strstr(name, "std::basic_string") != NULL) + { + annobin_gnu_compiler_flags &= ~GNU_PROPERTY_USECXX11_ABI; + annobin_gnu_compiler_flags_sets |= GNU_PROPERTY_USECXX11_ABI; + } + + /* Search the C++ standard usage */ + char *std_list = strstr((char *)name, "_List_base"); + if (std_list) + { + /* search a prefix */ + char *cxx_str = strstr((char *)name, "__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 @@ -1861,14 +1843,10 @@ annobin_prepare_dynamic_notes (void) 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); + if (ANNOBIN_SANITIZER_VALIDATION) { annobin_gnu_compiler_flags |= GNU_PROPERTY_SANITIZE_VALIDATION; annobin_inform(1, "Enable sanitizer validation"); @@ -2063,6 +2041,10 @@ annobin_init (void) annobin_record_global_options (); if (annobin_static_notes_p()) annobin_create_global_build_attributes (); + + /* CXX11 ABI is default value since GCC 5.x. */ + if (annobin_dynamic_notes_p ()) + annobin_gnu_compiler_flags |= GNU_PROPERTY_USECXX11_ABI; } void diff --git a/gcc/annobin.h b/gcc/annobin.h index d34c0b7..63eb783 100644 --- a/gcc/annobin.h +++ b/gcc/annobin.h @@ -123,7 +123,7 @@ extern void annobin_assemble_end_function_notes (); /* Generate final annobin annotations. */ extern void annobin_finish_file (); -extern void annobin_search_abi_declaration (tree decl); +extern void annobin_search_abi_declaration (tree decl, const char *name); /* Declare initial static sections for now, compute flags. */ extern void annobin_init (); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 5b5505c..db892de 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -7016,7 +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); + annobin_search_abi_declaration (decl, decl_as_string(decl, 0)); invoke_plugin_callbacks (PLUGIN_FINISH_DECL, decl); } -- 2.7.4