From 9136aa49abcb3f23171e01ebd16ee03cc1032943 Mon Sep 17 00:00:00 2001 From: Dan Gisselquist Date: Thu, 18 Feb 2016 09:47:31 +0000 Subject: [PATCH] Avoid setting or recording negative alignments when the target stores multiple octets in a single byte. gas * read.c (finish_bundle): Avoid recording a negative alignment. (do_align): Use unsigned values for n, len and max. Only create a frag if the alignment requirement is greater than the minimum byte alignment. Avoid recording a negative alignment. (s_align): Use unsigned values where appropriate. (bss_alloc): Use an unsigned value for the alignment. (sizeof_sleb128): Add a comment noting that we encode one octet per byte, regardless of the value of OCTETS_PER_BYTE_POWER. (emit_leb129_expr): Abort if the emitted encoding was longer than expected. * read.h (output_leb128): Update prototype. (sizeof_leb128): Update prototype. (bss_alloc): Update prototype. * write.c (record_alignment): Use an unsigned value for the alignment. Do not record alignments less than the minimum alignment for a byte. * write.h (record_alignment): Update prototype. --- gas/ChangeLog | 21 +++++ gas/read.c | 220 +++++++++++++++++++++++++++++--------------------- gas/read.h | 10 +-- gas/write.c | 7 +- gas/write.h | 6 +- 5 files changed, 160 insertions(+), 104 deletions(-) diff --git a/gas/ChangeLog b/gas/ChangeLog index 3688728f2a..7236c3ee82 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,24 @@ +2016-02-18 Dan Gisselquist + Nick Clifton + + * read.c (finish_bundle): Avoid recording a negative alignment. + (do_align): Use unsigned values for n, len and max. Only create + a frag if the alignment requirement is greater than the minimum + byte alignment. Avoid recording a negative alignment. + (s_align): Use unsigned values where appropriate. + (bss_alloc): Use an unsigned value for the alignment. + (sizeof_sleb128): Add a comment noting that we encode one octet + per byte, regardless of the value of OCTETS_PER_BYTE_POWER. + (emit_leb129_expr): Abort if the emitted encoding was longer than + expected. + * read.h (output_leb128): Update prototype. + (sizeof_leb128): Update prototype. + (bss_alloc): Update prototype. + * write.c (record_alignment): Use an unsigned value for the + alignment. Do not record alignments less than the minimum + alignment for a byte. + * write.h (record_alignment): Update prototype. + 2016-02-17 Max Filippov * config/tc-xtensa.c (xtensa_move_literals): Fix check for diff --git a/gas/read.c b/gas/read.c index 5c04789972..f8fff782e0 100644 --- a/gas/read.c +++ b/gas/read.c @@ -238,7 +238,6 @@ static unsigned int bundle_lock_depth; #endif static void do_s_func (int end_p, const char *default_prefix); -static void do_align (int, char *, int, int); static void s_align (int, int); static void s_altmacro (int); static void s_bad_end (int); @@ -487,6 +486,7 @@ static offsetT get_absolute_expr (expressionS *exp) { expression_and_evaluate (exp); + if (exp->X_op != O_constant) { if (exp->X_op != O_absent) @@ -685,7 +685,8 @@ finish_bundle (fragS *frag, unsigned int size) /* We do this every time rather than just in s_bundle_align_mode so that we catch any affected section without needing hooks all over for all paths that do section changes. It's cheap enough. */ - record_alignment (now_seg, bundle_align_p2 - OCTETS_PER_BYTE_POWER); + if (bundle_align_p2 > OCTETS_PER_BYTE_POWER) + record_alignment (now_seg, bundle_align_p2 - OCTETS_PER_BYTE_POWER); } /* Assemble one instruction. This takes care of the bundle features @@ -736,6 +737,75 @@ single instruction is %u bytes long but .bundle_align_mode limit is %u"), #endif /* HANDLE_BUNDLE */ +static bfd_boolean +in_bss (void) +{ + flagword flags = bfd_get_section_flags (stdoutput, now_seg); + + return (flags & SEC_ALLOC) && !(flags & (SEC_LOAD | SEC_HAS_CONTENTS)); +} + +/* Guts of .align directive: + N is the power of two to which to align. A value of zero is accepted but + ignored: the default alignment of the section will be at least this. + FILL may be NULL, or it may point to the bytes of the fill pattern. + LEN is the length of whatever FILL points to, if anything. If LEN is zero + but FILL is not NULL then LEN is treated as if it were one. + MAX is the maximum number of characters to skip when doing the alignment, + or 0 if there is no maximum. */ + +static void +do_align (unsigned int n, char *fill, unsigned int len, unsigned int max) +{ + if (now_seg == absolute_section || in_bss ()) + { + if (fill != NULL) + while (len-- > 0) + if (*fill++ != '\0') + { + if (now_seg == absolute_section) + as_warn (_("ignoring fill value in absolute section")); + else + as_warn (_("ignoring fill value in section `%s'"), + segment_name (now_seg)); + break; + } + fill = NULL; + len = 0; + } + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + +#ifdef md_do_align + md_do_align (n, fill, len, max, just_record_alignment); +#endif + + /* Only make a frag if we HAVE to... */ + if ((n > OCTETS_PER_BYTE_POWER) && !need_pass_2) + { + if (fill == NULL) + { + if (subseg_text_p (now_seg)) + frag_align_code (n, max); + else + frag_align (n, 0, max); + } + else if (len <= 1) + frag_align (n, *fill, max); + else + frag_align_pattern (n, fill, len, max); + } + +#ifdef md_do_align + just_record_alignment: ATTRIBUTE_UNUSED_LABEL +#endif + + if (n > OCTETS_PER_BYTE_POWER) + record_alignment (now_seg, n - OCTETS_PER_BYTE_POWER); +} + /* We read the file, putting things into a web that represents what we have been reading. */ void @@ -1337,14 +1407,6 @@ convert_to_bignum (expressionS *exp, int sign) exp->X_add_number = i; } -static bfd_boolean -in_bss (void) -{ - flagword flags = bfd_get_section_flags (stdoutput, now_seg); - - return (flags & SEC_ALLOC) && !(flags & (SEC_LOAD | SEC_HAS_CONTENTS)); -} - /* For most MRI pseudo-ops, the line actually ends at the first nonquoted space. This function looks for that point, stuffs a null in, and sets *STOPCP to the character that used to be there, and @@ -1401,62 +1463,6 @@ s_abort (int ignore ATTRIBUTE_UNUSED) as_fatal (_(".abort detected. Abandoning ship.")); } -/* Guts of .align directive. N is the power of two to which to align. - FILL may be NULL, or it may point to the bytes of the fill pattern. - LEN is the length of whatever FILL points to, if anything. MAX is - the maximum number of characters to skip when doing the alignment, - or 0 if there is no maximum. */ - -static void -do_align (int n, char *fill, int len, int max) -{ - if (now_seg == absolute_section || in_bss ()) - { - if (fill != NULL) - while (len-- > 0) - if (*fill++ != '\0') - { - if (now_seg == absolute_section) - as_warn (_("ignoring fill value in absolute section")); - else - as_warn (_("ignoring fill value in section `%s'"), - segment_name (now_seg)); - break; - } - fill = NULL; - len = 0; - } - -#ifdef md_flush_pending_output - md_flush_pending_output (); -#endif -#ifdef md_do_align - md_do_align (n, fill, len, max, just_record_alignment); -#endif - - /* Only make a frag if we HAVE to... */ - if (n != 0 && !need_pass_2) - { - if (fill == NULL) - { - if (subseg_text_p (now_seg)) - frag_align_code (n, max); - else - frag_align (n, 0, max); - } - else if (len <= 1) - frag_align (n, *fill, max); - else - frag_align_pattern (n, fill, len, max); - } - -#ifdef md_do_align - just_record_alignment: ATTRIBUTE_UNUSED_LABEL -#endif - - record_alignment (now_seg, n - OCTETS_PER_BYTE_POWER); -} - /* Handle the .align pseudo-op. A positive ARG is a default alignment (in bytes). A negative ARG is the negative of the length of the fill pattern. BYTES_P is non-zero if the alignment value should be @@ -1466,14 +1472,14 @@ do_align (int n, char *fill, int len, int max) #endif static void -s_align (int arg, int bytes_p) +s_align (signed int arg, int bytes_p) { unsigned int align_limit = TC_ALIGN_LIMIT; unsigned int align; char *stop = NULL; char stopc = 0; offsetT fill = 0; - int max; + unsigned int max; int fill_p; if (flag_mri) @@ -1553,15 +1559,16 @@ s_align (int arg, int bytes_p) } else { - int fill_len; + unsigned int fill_len; if (arg >= 0) fill_len = 1; else fill_len = -arg; + if (fill_len <= 1) { - char fill_char; + char fill_char = 0; fill_char = fill; do_align (align, &fill_char, fill_len, max); @@ -1571,7 +1578,12 @@ s_align (int arg, int bytes_p) char ab[16]; if ((size_t) fill_len > sizeof ab) - abort (); + { + as_warn (_("fill pattern too long, truncating to %u"), + (unsigned) sizeof ab); + fill_len = sizeof ab; + } + md_number_to_chars (ab, fill, fill_len); do_align (align, ab, fill_len, max); } @@ -2429,7 +2441,7 @@ s_linkonce (int ignore ATTRIBUTE_UNUSED) } void -bss_alloc (symbolS *symbolP, addressT size, int align) +bss_alloc (symbolS *symbolP, addressT size, unsigned int align) { char *pfrag; segT current_seg = now_seg; @@ -2453,7 +2465,7 @@ bss_alloc (symbolS *symbolP, addressT size, int align) #endif subseg_set (bss_seg, 1); - if (align) + if (align > OCTETS_PER_BYTE_POWER) { record_alignment (bss_seg, align); frag_align (align, 0, 0); @@ -2907,7 +2919,7 @@ s_mri_sect (char *type ATTRIBUTE_UNUSED) if (c == ',') { - int align; + unsigned int align; ++input_line_pointer; align = get_absolute_expression (); @@ -3006,7 +3018,7 @@ s_mri_sect (char *type ATTRIBUTE_UNUSED) } else if (strcasecmp (seccmd, "align") == 0) { - int align; + unsigned int align; (void) restore_line_pointer (c); align = get_absolute_expression (); @@ -4993,9 +5005,25 @@ float_cons (/* Clobbers input_line-pointer, checks end-of-line. */ demand_empty_rest_of_line (); } -/* Return the size of a LEB128 value. */ +/* LEB128 Encoding. -static inline int + Note - we are using the DWARF standard's definition of LEB128 encoding + where each 7-bit value is a stored in a byte, *not* an octet. This + means that on targets where a byte contains multiple octets there is + a *huge waste of space*. (This also means that we do not have to + have special versions of these functions for when OCTETS_PER_BYTE_POWER + is non-zero). + + If the 7-bit values were to be packed into N-bit bytes (where N > 8) + we would then have to consider whether multiple, successive LEB128 + values should be packed into the bytes without padding (bad idea) or + whether each LEB128 number is padded out to a whole number of bytes. + Plus you have to decide on the endianness of packing octets into a + byte. */ + +/* Return the size of a LEB128 value in bytes. */ + +static inline unsigned int sizeof_sleb128 (offsetT value) { int size = 0; @@ -5016,7 +5044,7 @@ sizeof_sleb128 (offsetT value) return size; } -static inline int +static inline unsigned int sizeof_uleb128 (valueT value) { int size = 0; @@ -5031,7 +5059,7 @@ sizeof_uleb128 (valueT value) return size; } -int +unsigned int sizeof_leb128 (valueT value, int sign) { if (sign) @@ -5040,9 +5068,9 @@ sizeof_leb128 (valueT value, int sign) return sizeof_uleb128 (value); } -/* Output a LEB128 value. */ +/* Output a LEB128 value. Returns the number of bytes used. */ -static inline int +static inline unsigned int output_sleb128 (char *p, offsetT value) { char *orig = p; @@ -5069,7 +5097,7 @@ output_sleb128 (char *p, offsetT value) return p - orig; } -static inline int +static inline unsigned int output_uleb128 (char *p, valueT value) { char *orig = p; @@ -5077,6 +5105,7 @@ output_uleb128 (char *p, valueT value) do { unsigned byte = (value & 0x7f); + value >>= 7; if (value != 0) /* More bytes to follow. */ @@ -5089,7 +5118,7 @@ output_uleb128 (char *p, valueT value) return p - orig; } -int +unsigned int output_leb128 (char *p, valueT value, int sign) { if (sign) @@ -5100,10 +5129,11 @@ output_leb128 (char *p, valueT value, int sign) /* Do the same for bignums. We combine sizeof with output here in that we don't output for NULL values of P. It isn't really as critical as - for "normal" values that this be streamlined. */ + for "normal" values that this be streamlined. Returns the number of + bytes used. */ -static inline int -output_big_sleb128 (char *p, LITTLENUM_TYPE *bignum, int size) +static inline unsigned int +output_big_sleb128 (char *p, LITTLENUM_TYPE *bignum, unsigned int size) { char *orig = p; valueT val = 0; @@ -5157,8 +5187,8 @@ output_big_sleb128 (char *p, LITTLENUM_TYPE *bignum, int size) return p - orig; } -static inline int -output_big_uleb128 (char *p, LITTLENUM_TYPE *bignum, int size) +static inline unsigned int +output_big_uleb128 (char *p, LITTLENUM_TYPE *bignum, unsigned int size) { char *orig = p; valueT val = 0; @@ -5196,8 +5226,8 @@ output_big_uleb128 (char *p, LITTLENUM_TYPE *bignum, int size) return p - orig; } -static int -output_big_leb128 (char *p, LITTLENUM_TYPE *bignum, int size, int sign) +static unsigned int +output_big_leb128 (char *p, LITTLENUM_TYPE *bignum, unsigned int size, int sign) { if (sign) return output_big_sleb128 (p, bignum, size); @@ -5270,23 +5300,25 @@ emit_leb128_expr (expressionS *exp, int sign) /* If we've got a constant, emit the thing directly right now. */ valueT value = exp->X_add_number; - int size; + unsigned int size; char *p; size = sizeof_leb128 (value, sign); p = frag_more (size); - output_leb128 (p, value, sign); + if (output_leb128 (p, value, sign) > size) + abort (); } else if (op == O_big) { /* O_big is a different sort of constant. */ - int size; + unsigned int size; char *p; size = output_big_leb128 (NULL, generic_bignum, exp->X_add_number, sign); p = frag_more (size); - output_big_leb128 (p, generic_bignum, exp->X_add_number, sign); + if (output_big_leb128 (p, generic_bignum, exp->X_add_number, sign) > size) + abort (); } else { diff --git a/gas/read.h b/gas/read.h index c1b040a5d6..146dc3657c 100644 --- a/gas/read.h +++ b/gas/read.h @@ -128,17 +128,17 @@ extern void emit_expr_with_reloc (expressionS *exp, unsigned int nbytes, TC_PARSE_CONS_RETURN_TYPE); extern void emit_expr_fix (expressionS *, unsigned int, fragS *, char *, TC_PARSE_CONS_RETURN_TYPE); -extern void equals (char *sym_name, int reassign); -extern void float_cons (int float_type); +extern void equals (char *, int); +extern void float_cons (int); extern void ignore_rest_of_line (void); #define discard_rest_of_line ignore_rest_of_line -extern int output_leb128 (char *, valueT, int sign); +extern unsigned output_leb128 (char *, valueT, int); extern void pseudo_set (symbolS * symbolP); extern void read_a_source_file (char *name); extern void read_begin (void); extern void read_print_statistics (FILE *); extern char *read_symbol_name (void); -extern int sizeof_leb128 (valueT, int sign); +extern unsigned sizeof_leb128 (valueT, int); extern void stabs_generate_asm_file (void); extern void stabs_generate_asm_lineno (void); extern void stabs_generate_asm_func (const char *, const char *); @@ -153,7 +153,7 @@ extern void generate_lineno_debug (void); extern void s_abort (int) ATTRIBUTE_NORETURN; extern void s_align_bytes (int arg); extern void s_align_ptwo (int); -extern void bss_alloc (symbolS *, addressT, int); +extern void bss_alloc (symbolS *, addressT, unsigned); extern offsetT parse_align (int); extern symbolS *s_comm_internal (int, symbolS *(*) (int, symbolS *, addressT)); extern symbolS *s_lcomm_internal (int, symbolS *, addressT); diff --git a/gas/write.c b/gas/write.c index f9d5da9c19..46d8d96ab6 100644 --- a/gas/write.c +++ b/gas/write.c @@ -355,12 +355,15 @@ record_alignment (/* Segment to which alignment pertains. */ segT seg, /* Alignment, as a power of 2 (e.g., 1 => 2-byte boundary, 2 => 4-byte boundary, etc.) */ - int align) + unsigned int align) { if (seg == absolute_section) return; - if ((unsigned int) align > bfd_get_section_alignment (stdoutput, seg)) + if (align <= OCTETS_PER_BYTE_POWER) + return; + + if (align > bfd_get_section_alignment (stdoutput, seg)) bfd_set_section_alignment (stdoutput, seg, align); } diff --git a/gas/write.h b/gas/write.h index b1bc778f74..4aee78d039 100644 --- a/gas/write.h +++ b/gas/write.h @@ -166,9 +166,9 @@ extern addressT dot_value; extern fragS *dot_frag; extern struct reloc_list* reloc_list; -extern void append (char **charPP, char *fromP, unsigned long length); -extern void record_alignment (segT seg, int align); -extern int get_recorded_alignment (segT seg); +extern void append (char **, char *, unsigned long); +extern void record_alignment (segT, unsigned); +extern int get_recorded_alignment (segT); extern void write_object_file (void); extern long relax_frag (segT, fragS *, long); extern int relax_segment (struct frag *, segT, int); -- 2.34.1