diff options
author | Mikhail Kashkarov <m.kashkarov@partner.samsung.com> | 2019-07-09 22:40:49 +0300 |
---|---|---|
committer | Mikhail Kashkarov <m.kashkarov@partner.samsung.com> | 2019-07-10 14:05:41 +0300 |
commit | ac067028cee3d094a90955ec12ca2257362e74e2 (patch) | |
tree | 4f5a8d30f4a93d454fff6325149599880b5be9f0 | |
parent | 8ec426d7ca264f19e419649820ad1c27012e46d7 (diff) | |
download | linaro-gcc-sandbox/mkashkarov/annotate_binary.tar.gz linaro-gcc-sandbox/mkashkarov/annotate_binary.tar.bz2 linaro-gcc-sandbox/mkashkarov/annotate_binary.zip |
Annobin srcsandbox/mkashkarov/annotate_binary
Change-Id: Ib3571622cd090992ce42305b7c5985fd322322d4
-rw-r--r-- | gcc/Makefile.in | 1 | ||||
-rw-r--r-- | gcc/annobin.c | 2079 | ||||
-rw-r--r-- | gcc/annobin.h | 152 | ||||
-rw-r--r-- | gcc/asan.h | 20 | ||||
-rw-r--r-- | gcc/cgraphunit.c | 4 | ||||
-rw-r--r-- | gcc/common.opt | 19 | ||||
-rw-r--r-- | gcc/cp/decl.c | 2 | ||||
-rw-r--r-- | gcc/final.c | 11 | ||||
-rw-r--r-- | gcc/flag-types.h | 9 | ||||
-rw-r--r-- | gcc/output.h | 1 | ||||
-rw-r--r-- | gcc/params.def | 15 | ||||
-rw-r--r-- | gcc/params.h | 6 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/annobin/annobin.exp | 35 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/annobin/build-attributes-1.c | 4 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/annobin/build-attributes-2.c | 5 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/annobin/build-attributes-3.c | 17 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/annobin/build-attributes-4.c | 14 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/annobin/build-attributes-5.c | 11 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/annobin/note-property-1.c | 6 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/annobin/note-property-2.c | 7 | ||||
-rw-r--r-- | gcc/toplev.c | 6 | ||||
-rw-r--r-- | gcc/varasm.c | 80 |
22 files changed, 2488 insertions, 16 deletions
diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 2e8fa8a9f7d..600fed101e3 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 00000000000..efd5873e07e --- /dev/null +++ b/gcc/annobin.c @@ -0,0 +1,2079 @@ +/* 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 <machmode.h> +#include <output.h> +#include <opts.h> +#include <toplev.h> +#include <function.h> +/* #include <defaults.h> */ +#include <tree.h> +/* 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) + { + fprintf (asm_out_file, "# desc1 begin\n"); + 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, "# desc1 end\n"); + } + + 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=<n> 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 (); +// fprintf (stderr, " Name '%s'\n", decl_str); + 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; + fprintf(asm_out_file, "# Annobin begin: %s\n", function_asm_name ()); + 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 (); + fprintf(asm_out_file, "# Annobin end : %s\n", function_asm_name ()); +} + +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 00000000000..d34c0b79806 --- /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 <elf.h> + +/* 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 86b295583cb..efc517e0556 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/cgraphunit.c b/gcc/cgraphunit.c index 71e88be753a..a5f4ea0cd79 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -1986,9 +1986,13 @@ cgraph_node::expand (void) /* Signal the start of passes. */ invoke_plugin_callbacks (PLUGIN_ALL_PASSES_START, NULL); + if (asm_out_file) + fprintf (asm_out_file, "# START HERE\n"); execute_pass_list (cfun, g->get_passes ()->all_passes); + if (asm_out_file) + fprintf (asm_out_file, "# END HERE\n"); /* Signal the end of passes. */ invoke_plugin_callbacks (PLUGIN_ALL_PASSES_END, NULL); diff --git a/gcc/common.opt b/gcc/common.opt index 44292ad38d1..826f3dbb49b 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_ALL) +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 95d7cf0eca7..5b5505c8a89 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/final.c b/gcc/final.c index 423550f5bdb..3457c222916 100644 --- a/gcc/final.c +++ b/gcc/final.c @@ -1921,6 +1921,16 @@ final_end_function (void) dwarf2out_end_epilogue (last_linenum, last_filename); some_local_dynamic_name = 0; + + if (DECL_ASSEMBLER_NAME_SET_P (current_function_decl)) + { + fprintf(asm_out_file, "# | final_end_function: %s\n", + IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME (current_function_decl))); + } + else + { + fprintf(asm_out_file, "# | final_end_function: ???\n"); + } } @@ -4489,6 +4499,7 @@ rest_of_handle_final (void) targetm.asm_out.destructor (XEXP (DECL_RTL (current_function_decl), 0), decl_fini_priority_lookup (current_function_decl)); + fprintf(asm_out_file, "# \ rest_of_handle_final: %s\n", fnname); return 0; } diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 2a3a48ee441..559bed659b4 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 0924499c484..d9507b97ed6 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 c1d2e7b1ab5..3696af66686 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 4017c85d6b9..f943a4a0d41 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 00000000000..859799f4589 --- /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 +# <http://www.gnu.org/licenses/>. + +# 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 00000000000..0bede41ed34 --- /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 00000000000..73f5a792cc1 --- /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 00000000000..2532b34916f --- /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 00000000000..3b244d9f708 --- /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 00000000000..ee843f2dc10 --- /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 00000000000..3581168da05 --- /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 00000000000..5530307c77f --- /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 39692c42bc5..1e5280e3556 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,10 @@ compile_file (void) /* Invoke registered plugin callbacks. */ invoke_plugin_callbacks (PLUGIN_FINISH_UNIT, NULL); + /* Generate final binary annotations . */ + /* TODO: Could this be moved before final lto steps? */ + 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 +1996,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 2c48b931da1..80eb6d5a8b7 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 * @@ -1698,6 +1736,7 @@ assemble_start_function (tree decl, const char *fnname) char tmp_label[100]; bool hot_label_written = false; + fprintf(asm_out_file, "# / assemble_start_function: %s\n", fnname); if (flag_reorder_blocks_and_partition) { ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LHOTB", const_labelno); @@ -1723,6 +1762,10 @@ assemble_start_function (tree decl, const char *fnname) app_disable (); + + fprintf (asm_out_file, "# Begin assemble_start_function\n"); + annobin_assemble_start_function_notes (); + if (CONSTANT_POOL_BEFORE_FUNCTION) output_constant_pool (fnname, decl); @@ -1820,6 +1863,8 @@ assemble_start_function (tree decl, const char *fnname) if (lookup_attribute ("no_split_stack", DECL_ATTRIBUTES (decl))) saw_no_split_stack = true; + + fprintf (asm_out_file, "# End assemble_start_function\n"); } /* Output assembler code associated with defining the size of the @@ -1861,6 +1906,9 @@ 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); } + + fprintf(asm_out_file, "# \ assemble_end_function: %s\n", fnname); + annobin_assemble_end_function_notes (); } /* Assemble code to leave SIZE bytes of zeros. */ |