X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gas%2Fdwarf2dbg.c;h=6a66225b54a8b57906c1e48db2141034115eadc5;hb=a09f656b267b9a684f038fba7cadfe98e2f18892;hp=e02b6e8f020fa19c7e6d912f6ad67f86080e8785;hpb=49fced1206db40c71208c201165d65f92c69cebe;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/dwarf2dbg.c b/gas/dwarf2dbg.c index e02b6e8f02..6a66225b54 100644 --- a/gas/dwarf2dbg.c +++ b/gas/dwarf2dbg.c @@ -1,5 +1,5 @@ /* dwarf2dbg.c - DWARF2 debug support - Copyright (C) 1999-2017 Free Software Foundation, Inc. + Copyright (C) 1999-2020 Free Software Foundation, Inc. Contributed by David Mosberger-Tang This file is part of GAS, the GNU Assembler. @@ -81,15 +81,15 @@ #endif #ifndef DWARF2_FILE_TIME_NAME -#define DWARF2_FILE_TIME_NAME(FILENAME,DIRNAME) 0 +#define DWARF2_FILE_TIME_NAME(FILENAME,DIRNAME) -1 #endif #ifndef DWARF2_FILE_SIZE_NAME -#define DWARF2_FILE_SIZE_NAME(FILENAME,DIRNAME) 0 +#define DWARF2_FILE_SIZE_NAME(FILENAME,DIRNAME) -1 #endif #ifndef DWARF2_VERSION -#define DWARF2_VERSION 2 +#define DWARF2_VERSION dwarf_level #endif /* The .debug_aranges version has been 2 in DWARF version 2, 3 and 4. */ @@ -97,9 +97,9 @@ #define DWARF2_ARANGES_VERSION 2 #endif -/* This implementation output version 2 .debug_line information. */ +/* This implementation outputs version 3 .debug_line information. */ #ifndef DWARF2_LINE_VERSION -#define DWARF2_LINE_VERSION 2 +#define DWARF2_LINE_VERSION (dwarf_level > 3 ? dwarf_level : 3) #endif #include "subsegs.h" @@ -147,6 +147,10 @@ /* Flag that indicates the initial value of the is_stmt_start flag. */ #define DWARF2_LINE_DEFAULT_IS_STMT 1 +#ifndef DWARF2_LINE_MAX_OPS_PER_INSN +#define DWARF2_LINE_MAX_OPS_PER_INSN 1 +#endif + /* Given a special op, return the line skip amount. */ #define SPECIAL_LINE(op) \ (((op) - DWARF2_LINE_OPCODE_BASE)%DWARF2_LINE_RANGE + DWARF2_LINE_BASE) @@ -162,13 +166,20 @@ #define TC_PARSE_CONS_RETURN_NONE BFD_RELOC_NONE #endif -struct line_entry { +struct line_entry +{ struct line_entry *next; symbolS *label; struct dwarf2_line_info loc; }; -struct line_subseg { +/* Don't change the offset of next in line_entry. set_or_check_view + calls in dwarf2_gen_line_info_1 depend on it. */ +static char unused[offsetof(struct line_entry, next) ? -1 : 1] +ATTRIBUTE_UNUSED; + +struct line_subseg +{ struct line_subseg *next; subsegT subseg; struct line_entry *head; @@ -176,7 +187,8 @@ struct line_subseg { struct line_entry **pmove_tail; }; -struct line_seg { +struct line_seg +{ struct line_seg *next; segT seg; struct line_subseg *head; @@ -188,9 +200,13 @@ struct line_seg { static struct line_seg *all_segs; static struct line_seg **last_seg_ptr; -struct file_entry { - const char *filename; - unsigned int dir; +#define NUM_MD5_BYTES 16 + +struct file_entry +{ + const char * filename; + unsigned int dir; + unsigned char md5[NUM_MD5_BYTES]; }; /* Table of files used by .debug_line. */ @@ -199,9 +215,9 @@ static unsigned int files_in_use; static unsigned int files_allocated; /* Table of directories used by .debug_line. */ -static char **dirs; -static unsigned int dirs_in_use; -static unsigned int dirs_allocated; +static char ** dirs = NULL; +static unsigned int dirs_in_use = 0; +static unsigned int dirs_allocated = 0; /* TRUE when we've seen a .loc directive recently. Used to avoid doing work when there's nothing to do. */ @@ -212,17 +228,24 @@ bfd_boolean dwarf2_loc_directive_seen; bfd_boolean dwarf2_loc_mark_labels; /* Current location as indicated by the most recent .loc directive. */ -static struct dwarf2_line_info current = { +static struct dwarf2_line_info current = +{ 1, 1, 0, 0, DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0, - 0 + 0, NULL }; +/* This symbol is used to recognize view number forced resets in loc + lists. */ +static symbolS *force_reset_view; + +/* This symbol evaluates to an expression that, if nonzero, indicates + some view assert check failed. */ +static symbolS *view_assert_failed; + /* The size of an address on the target. */ static unsigned int sizeof_address; -static unsigned int get_filenum (const char *, unsigned int); - #ifndef TC_DWARF2_EMIT_OFFSET #define TC_DWARF2_EMIT_OFFSET generic_dwarf2_emit_offset @@ -233,6 +256,7 @@ generic_dwarf2_emit_offset (symbolS *symbol, unsigned int size) { expressionS exp; + memset (&exp, 0, sizeof exp); exp.X_op = O_symbol; exp.X_add_symbol = symbol; exp.X_add_number = 0; @@ -261,6 +285,7 @@ get_line_subseg (segT seg, subsegT subseg, bfd_boolean create_p) last_seg_ptr = &s->next; seg_info (seg)->dwarf2_line_seg = s; } + gas_assert (seg == s->seg); for (pss = &s->head; (lss = *pss) != NULL ; pss = &lss->next) @@ -283,6 +308,183 @@ get_line_subseg (segT seg, subsegT subseg, bfd_boolean create_p) return lss; } +/* (Un)reverse the line_entry list starting from H. */ + +static struct line_entry * +reverse_line_entry_list (struct line_entry *h) +{ + struct line_entry *p = NULL, *e, *n; + + for (e = h; e; e = n) + { + n = e->next; + e->next = p; + p = e; + } + return p; +} + +/* Compute the view for E based on the previous entry P. If we + introduce an (undefined) view symbol for P, and H is given (P must + be the tail in this case), introduce view symbols for earlier list + entries as well, until one of them is constant. */ + +static void +set_or_check_view (struct line_entry *e, struct line_entry *p, + struct line_entry *h) +{ + expressionS viewx; + + memset (&viewx, 0, sizeof (viewx)); + viewx.X_unsigned = 1; + + /* First, compute !(E->label > P->label), to tell whether or not + we're to reset the view number. If we can't resolve it to a + constant, keep it symbolic. */ + if (!p || (e->loc.view == force_reset_view && force_reset_view)) + { + viewx.X_op = O_constant; + viewx.X_add_number = 0; + viewx.X_add_symbol = NULL; + viewx.X_op_symbol = NULL; + } + else + { + viewx.X_op = O_gt; + viewx.X_add_number = 0; + viewx.X_add_symbol = e->label; + viewx.X_op_symbol = p->label; + resolve_expression (&viewx); + if (viewx.X_op == O_constant) + viewx.X_add_number = !viewx.X_add_number; + else + { + viewx.X_add_symbol = make_expr_symbol (&viewx); + viewx.X_add_number = 0; + viewx.X_op_symbol = NULL; + viewx.X_op = O_logical_not; + } + } + + if (S_IS_DEFINED (e->loc.view) && symbol_constant_p (e->loc.view)) + { + expressionS *value = symbol_get_value_expression (e->loc.view); + /* We can't compare the view numbers at this point, because in + VIEWX we've only determined whether we're to reset it so + far. */ + if (viewx.X_op == O_constant) + { + if (!value->X_add_number != !viewx.X_add_number) + as_bad (_("view number mismatch")); + } + /* Record the expression to check it later. It is the result of + a logical not, thus 0 or 1. We just add up all such deferred + expressions, and resolve it at the end. */ + else if (!value->X_add_number) + { + symbolS *deferred = make_expr_symbol (&viewx); + if (view_assert_failed) + { + expressionS chk; + + memset (&chk, 0, sizeof (chk)); + chk.X_unsigned = 1; + chk.X_op = O_add; + chk.X_add_number = 0; + chk.X_add_symbol = view_assert_failed; + chk.X_op_symbol = deferred; + deferred = make_expr_symbol (&chk); + } + view_assert_failed = deferred; + } + } + + if (viewx.X_op != O_constant || viewx.X_add_number) + { + expressionS incv; + + if (!p->loc.view) + { + p->loc.view = symbol_temp_make (); + gas_assert (!S_IS_DEFINED (p->loc.view)); + } + + memset (&incv, 0, sizeof (incv)); + incv.X_unsigned = 1; + incv.X_op = O_symbol; + incv.X_add_symbol = p->loc.view; + incv.X_add_number = 1; + + if (viewx.X_op == O_constant) + { + gas_assert (viewx.X_add_number == 1); + viewx = incv; + } + else + { + viewx.X_add_symbol = make_expr_symbol (&viewx); + viewx.X_add_number = 0; + viewx.X_op_symbol = make_expr_symbol (&incv); + viewx.X_op = O_multiply; + } + } + + if (!S_IS_DEFINED (e->loc.view)) + { + symbol_set_value_expression (e->loc.view, &viewx); + S_SET_SEGMENT (e->loc.view, expr_section); + symbol_set_frag (e->loc.view, &zero_address_frag); + } + + /* Define and attempt to simplify any earlier views needed to + compute E's. */ + if (h && p && p->loc.view && !S_IS_DEFINED (p->loc.view)) + { + struct line_entry *h2; + /* Reverse the list to avoid quadratic behavior going backwards + in a single-linked list. */ + struct line_entry *r = reverse_line_entry_list (h); + + gas_assert (r == p); + /* Set or check views until we find a defined or absent view. */ + do + { + /* Do not define the head of a (sub?)segment view while + handling others. It would be defined too early, without + regard to the last view of other subsegments. + set_or_check_view will be called for every head segment + that needs it. */ + if (r == h) + break; + set_or_check_view (r, r->next, NULL); + } + while (r->next && r->next->loc.view && !S_IS_DEFINED (r->next->loc.view) + && (r = r->next)); + + /* Unreverse the list, so that we can go forward again. */ + h2 = reverse_line_entry_list (p); + gas_assert (h2 == h); + + /* Starting from the last view we just defined, attempt to + simplify the view expressions, until we do so to P. */ + do + { + /* The head view of a subsegment may remain undefined while + handling other elements, before it is linked to the last + view of the previous subsegment. */ + if (r == h) + continue; + gas_assert (S_IS_DEFINED (r->loc.view)); + resolve_expression (symbol_get_value_expression (r->loc.view)); + } + while (r != p && (r = r->next)); + + /* Now that we've defined and computed all earlier views that might + be needed to compute E's, attempt to simplify it. */ + resolve_expression (symbol_get_value_expression (e->loc.view)); + } +} + /* Record an entry for LOC occurring at LABEL. */ static void @@ -297,6 +499,14 @@ dwarf2_gen_line_info_1 (symbolS *label, struct dwarf2_line_info *loc) e->loc = *loc; lss = get_line_subseg (now_seg, now_subseg, TRUE); + + /* Subseg heads are chained to previous subsegs in + dwarf2_finish. */ + if (loc->view && lss->head) + set_or_check_view (e, + (struct line_entry *)lss->ptail, + lss->head); + *lss->ptail = e; lss->ptail = &e->next; } @@ -312,7 +522,9 @@ dwarf2_gen_line_info (addressT ofs, struct dwarf2_line_info *loc) symbolS *sym; /* Early out for as-yet incomplete location information. */ - if (loc->filenum == 0 || loc->line == 0) + if (loc->line == 0) + return; + if (loc->filenum == 0 && DWARF2_LINE_VERSION < 5) return; /* Don't emit sequences of line symbols for the same line when the @@ -340,6 +552,323 @@ dwarf2_gen_line_info (addressT ofs, struct dwarf2_line_info *loc) dwarf2_gen_line_info_1 (sym, loc); } +static const char * +get_basename (const char * pathname) +{ + const char * file; + + file = lbasename (pathname); + /* Don't make empty string from / or A: from A:/ . */ +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + if (file <= pathname + 3) + file = pathname; +#else + if (file == pathname + 1) + file = pathname; +#endif + return file; +} + +static unsigned int +get_directory_table_entry (const char * dirname, + size_t dirlen, + bfd_boolean can_use_zero) +{ + unsigned int d; + + if (dirlen == 0) + return 0; + +#ifndef DWARF2_DIR_SHOULD_END_WITH_SEPARATOR + if (IS_DIR_SEPARATOR (dirname[dirlen - 1])) + { + -- dirlen; + if (dirlen == 0) + return 0; + } +#endif + + for (d = 0; d < dirs_in_use; ++d) + { + if (dirs[d] != NULL + && filename_ncmp (dirname, dirs[d], dirlen) == 0 + && dirs[d][dirlen] == '\0') + return d; + } + + if (can_use_zero) + { + if (dirs == NULL || dirs[0] == NULL) + d = 0; + } + else if (d == 0) + d = 1; + + if (d >= dirs_allocated) + { + unsigned int old = dirs_allocated; + + dirs_allocated = d + 32; + dirs = XRESIZEVEC (char *, dirs, dirs_allocated); + memset (dirs + old, 0, (dirs_allocated - old) * sizeof (char *)); + } + + dirs[d] = xmemdup0 (dirname, dirlen); + if (dirs_in_use <= d) + dirs_in_use = d + 1; + + return d; +} + +/* Get a .debug_line file number for PATHNAME. If there is a + directory component to PATHNAME, then this will be stored + in the directory table, if it is not already present. + Returns the slot number allocated to that filename or -1 + if there was a problem. */ + +static signed int +allocate_filenum (const char * pathname) +{ + static signed int last_used = -1, last_used_dir_len = 0; + const char *file; + size_t dir_len; + unsigned int i, dir; + + /* Short circuit the common case of adding the same pathname + as last time. */ + if (last_used != -1) + { + const char * dirname = NULL; + + if (dirs != NULL) + dirname = dirs[files[last_used].dir]; + + if (dirname == NULL) + { + if (filename_cmp (pathname, files[last_used].filename) == 0) + return last_used; + } + else + { + if (filename_ncmp (pathname, dirname, last_used_dir_len) == 0 + && IS_DIR_SEPARATOR (pathname [last_used_dir_len]) + && filename_cmp (pathname + last_used_dir_len + 1, + files[last_used].filename) == 0) + return last_used; + } + } + + file = get_basename (pathname); + dir_len = file - pathname; + + dir = get_directory_table_entry (pathname, dir_len, FALSE); + + /* Do not use slot-0. That is specificailly reserved for use by + the '.file 0 "name"' directive. */ + for (i = 1; i < files_in_use; ++i) + if (files[i].dir == dir + && files[i].filename + && filename_cmp (file, files[i].filename) == 0) + { + last_used = i; + last_used_dir_len = dir_len; + return i; + } + + if (i >= files_allocated) + { + unsigned int old = files_allocated; + + files_allocated = i + 32; + /* Catch wraparound. */ + if (files_allocated <= old) + { + as_bad (_("file number %lu is too big"), (unsigned long) i); + return -1; + } + + files = XRESIZEVEC (struct file_entry, files, files_allocated); + memset (files + old, 0, (i + 32 - old) * sizeof (struct file_entry)); + } + + files[i].filename = file; + files[i].dir = dir; + memset (files[i].md5, 0, NUM_MD5_BYTES); + + if (files_in_use < i + 1) + files_in_use = i + 1; + last_used = i; + last_used_dir_len = dir_len; + + return i; +} + +/* Allocate slot NUM in the .debug_line file table to FILENAME. + If DIRNAME is not NULL or there is a directory component to FILENAME + then this will be stored in the directory table, if not already present. + if WITH_MD5 is TRUE then there is a md5 value in generic_bignum. + Returns TRUE if allocation succeeded, FALSE otherwise. */ + +static bfd_boolean +allocate_filename_to_slot (const char * dirname, + const char * filename, + unsigned int num, + bfd_boolean with_md5) +{ + const char *file; + size_t dirlen; + unsigned int i, d; + + /* Short circuit the common case of adding the same pathname + as last time. */ + if (num < files_allocated && files[num].filename != NULL) + { + const char * dir = NULL; + + if (dirs) + dir = dirs[files[num].dir]; + + if (with_md5 + && memcmp (generic_bignum, files[num].md5, NUM_MD5_BYTES) != 0) + goto fail; + + if (dirname != NULL) + { + if (dir != NULL && filename_cmp (dir, dirname) != 0) + goto fail; + + if (filename_cmp (filename, files[num].filename) != 0) + goto fail; + + /* If the filenames match, but the directory table entry was + empty, then fill it with the provided directory name. */ + if (dir == NULL) + dirs[files[num].dir] = xmemdup0 (dirname, strlen (dirname)); + + return TRUE; + } + else if (dir != NULL) + { + dirlen = strlen (dir); + if (filename_ncmp (filename, dir, dirlen) == 0 + && IS_DIR_SEPARATOR (filename [dirlen]) + && filename_cmp (filename + dirlen + 1, files[num].filename) == 0) + return TRUE; + } + else /* dir == NULL */ + { + file = get_basename (filename); + if (filename_cmp (file, files[num].filename) == 0) + { + if (file > filename) + /* The filenames match, but the directory table entry is empty. + Fill it with the provided directory name. */ + dirs[files[num].dir] = xmemdup0 (filename, file - filename); + return TRUE; + } + } + + fail: + as_bad (_("file table slot %u is already occupied by a different file (%s%s%s vs %s%s%s)"), + num, + dir == NULL ? "" : dir, + dir == NULL ? "" : "/", + files[num].filename, + dirname == NULL ? "" : dirname, + dirname == NULL ? "" : "/", + filename); + return FALSE; + } + + if (dirname == NULL) + { + dirname = filename; + file = get_basename (filename); + dirlen = file - filename; + } + else + { + dirlen = strlen (dirname); + file = filename; + } + + d = get_directory_table_entry (dirname, dirlen, num == 0); + i = num; + + if (i >= files_allocated) + { + unsigned int old = files_allocated; + + files_allocated = i + 32; + /* Catch wraparound. */ + if (files_allocated <= old) + { + as_bad (_("file number %lu is too big"), (unsigned long) i); + return FALSE; + } + + files = XRESIZEVEC (struct file_entry, files, files_allocated); + memset (files + old, 0, (i + 32 - old) * sizeof (struct file_entry)); + } + + files[i].filename = file; + files[i].dir = d; + if (with_md5) + { + if (target_big_endian) + { + /* md5's are stored in litte endian format. */ + unsigned int bits_remaining = NUM_MD5_BYTES * BITS_PER_CHAR; + unsigned int byte = NUM_MD5_BYTES; + unsigned int bignum_index = 0; + + while (bits_remaining) + { + unsigned int bignum_bits_remaining = LITTLENUM_NUMBER_OF_BITS; + valueT bignum_value = generic_bignum [bignum_index]; + bignum_index ++; + + while (bignum_bits_remaining) + { + files[i].md5[--byte] = bignum_value & 0xff; + bignum_value >>= 8; + bignum_bits_remaining -= 8; + bits_remaining -= 8; + } + } + } + else + { + unsigned int bits_remaining = NUM_MD5_BYTES * BITS_PER_CHAR; + unsigned int byte = 0; + unsigned int bignum_index = 0; + + while (bits_remaining) + { + unsigned int bignum_bits_remaining = LITTLENUM_NUMBER_OF_BITS; + valueT bignum_value = generic_bignum [bignum_index]; + + bignum_index ++; + + while (bignum_bits_remaining) + { + files[i].md5[byte++] = bignum_value & 0xff; + bignum_value >>= 8; + bignum_bits_remaining -= 8; + bits_remaining -= 8; + } + } + } + } + else + memset (files[i].md5, 0, NUM_MD5_BYTES); + + if (files_in_use < i + 1) + files_in_use = i + 1; + + return TRUE; +} + /* Returns the current source information. If .file directives have been encountered, the info for the corresponding source file is returned. Otherwise, the info for the assembly source file is @@ -350,12 +879,17 @@ dwarf2_where (struct dwarf2_line_info *line) { if (debug_type == DEBUG_DWARF2) { - const char *filename = as_where (&line->line); - line->filenum = get_filenum (filename, 0); + const char *filename; + + memset (line, 0, sizeof (*line)); + filename = as_where (&line->line); + line->filenum = allocate_filenum (filename); + /* FIXME: We should check the return value from allocate_filenum. */ line->column = 0; line->flags = DWARF2_FLAG_IS_STMT; line->isa = current.isa; line->discriminator = current.discriminator; + line->view = NULL; } else *line = current; @@ -380,12 +914,14 @@ dwarf2_emit_insn (int size) { struct dwarf2_line_info loc; - if (!dwarf2_loc_directive_seen && debug_type != DEBUG_DWARF2) + if (debug_type != DEBUG_DWARF2 + ? !dwarf2_loc_directive_seen + : !seen_at_least_1_file ()) return; dwarf2_where (&loc); - dwarf2_gen_line_info (frag_now_fix () - size, &loc); + dwarf2_gen_line_info ((frag_now_fix_octets () - size) / OCTETS_PER_BYTE, &loc); dwarf2_consume_line_info (); } @@ -432,6 +968,7 @@ dwarf2_consume_line_info (void) | DWARF2_FLAG_PROLOGUE_END | DWARF2_FLAG_EPILOGUE_BEGIN); current.discriminator = 0; + current.view = NULL; } /* Called for each (preferably code) label. If dwarf2_loc_mark_labels @@ -446,7 +983,7 @@ dwarf2_emit_label (symbolS *label) return; if (S_GET_SEGMENT (label) != now_seg) return; - if (!(bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)) + if (!(bfd_section_flags (now_seg) & SEC_CODE)) return; if (files_in_use == 0 && debug_type != DEBUG_DWARF2) return; @@ -459,113 +996,19 @@ dwarf2_emit_label (symbolS *label) dwarf2_consume_line_info (); } -/* Get a .debug_line file number for FILENAME. If NUM is nonzero, - allocate it on that file table slot, otherwise return the first - empty one. */ - -static unsigned int -get_filenum (const char *filename, unsigned int num) -{ - static unsigned int last_used, last_used_dir_len; - const char *file; - size_t dir_len; - unsigned int i, dir; - - if (num == 0 && last_used) - { - if (! files[last_used].dir - && filename_cmp (filename, files[last_used].filename) == 0) - return last_used; - if (files[last_used].dir - && filename_ncmp (filename, dirs[files[last_used].dir], - last_used_dir_len) == 0 - && IS_DIR_SEPARATOR (filename [last_used_dir_len]) - && filename_cmp (filename + last_used_dir_len + 1, - files[last_used].filename) == 0) - return last_used; - } - - file = lbasename (filename); - /* Don't make empty string from / or A: from A:/ . */ -#ifdef HAVE_DOS_BASED_FILE_SYSTEM - if (file <= filename + 3) - file = filename; -#else - if (file == filename + 1) - file = filename; -#endif - dir_len = file - filename; - - dir = 0; - if (dir_len) - { -#ifndef DWARF2_DIR_SHOULD_END_WITH_SEPARATOR - --dir_len; -#endif - for (dir = 1; dir < dirs_in_use; ++dir) - if (filename_ncmp (filename, dirs[dir], dir_len) == 0 - && dirs[dir][dir_len] == '\0') - break; - - if (dir >= dirs_in_use) - { - if (dir >= dirs_allocated) - { - dirs_allocated = dir + 32; - dirs = XRESIZEVEC (char *, dirs, dirs_allocated); - } - - dirs[dir] = xmemdup0 (filename, dir_len); - dirs_in_use = dir + 1; - } - } - - if (num == 0) - { - for (i = 1; i < files_in_use; ++i) - if (files[i].dir == dir - && files[i].filename - && filename_cmp (file, files[i].filename) == 0) - { - last_used = i; - last_used_dir_len = dir_len; - return i; - } - } - else - i = num; - - if (i >= files_allocated) - { - unsigned int old = files_allocated; - - files_allocated = i + 32; - files = XRESIZEVEC (struct file_entry, files, files_allocated); - - memset (files + old, 0, (i + 32 - old) * sizeof (struct file_entry)); - } - - files[i].filename = num ? file : xstrdup (file); - files[i].dir = dir; - if (files_in_use < i + 1) - files_in_use = i + 1; - last_used = i; - last_used_dir_len = dir_len; - - return i; -} - /* Handle two forms of .file directive: - Pass .file "source.c" to s_app_file - Handle .file 1 "source.c" by adding an entry to the DWARF-2 file table - If an entry is added to the file table, return a pointer to the filename. */ + If an entry is added to the file table, return a pointer to the filename. */ char * -dwarf2_directive_file (int dummy ATTRIBUTE_UNUSED) +dwarf2_directive_filename (void) { - offsetT num; + bfd_boolean with_md5 = TRUE; + valueT num; char *filename; + const char * dirname = NULL; int filename_len; /* Continue to accept a bare string and pass it off. */ @@ -577,32 +1020,78 @@ dwarf2_directive_file (int dummy ATTRIBUTE_UNUSED) } num = get_absolute_expression (); + + if ((offsetT) num < 1 && DWARF2_LINE_VERSION < 5) + { + as_bad (_("file number less than one")); + ignore_rest_of_line (); + return NULL; + } + + /* FIXME: Should we allow ".file \n" as an expression meaning + "switch back to the already allocated file as the current + file" ? */ + filename = demand_copy_C_string (&filename_len); if (filename == NULL) + /* demand_copy_C_string will have already generated an error message. */ return NULL; - demand_empty_rest_of_line (); - if (num < 1) + /* For DWARF-5 support we also accept: + .file [""] "" [md5 ] */ + if (DWARF2_LINE_VERSION > 4) { - as_bad (_("file number less than one")); - return NULL; + SKIP_WHITESPACE (); + if (*input_line_pointer == '"') + { + dirname = filename; + filename = demand_copy_C_string (&filename_len); + SKIP_WHITESPACE (); + } + + if (strncmp (input_line_pointer, "md5", 3) == 0) + { + input_line_pointer += 3; + SKIP_WHITESPACE (); + + expressionS exp; + expression_and_evaluate (& exp); + if (exp.X_op != O_big) + as_bad (_("md5 value too small or not a constant")); + else + with_md5 = TRUE; + } } + demand_empty_rest_of_line (); + /* A .file directive implies compiler generated debug information is being supplied. Turn off gas generated debug info. */ debug_type = DEBUG_NONE; - if (num < (int) files_in_use && files[num].filename != 0) + if (num != (unsigned int) num + || num >= (size_t) -1 / sizeof (struct file_entry) - 32) { - as_bad (_("file number %ld already allocated"), (long) num); + as_bad (_("file number %lu is too big"), (unsigned long) num); return NULL; } - get_filenum (filename, num); + if (! allocate_filename_to_slot (dirname, filename, (unsigned int) num, + with_md5)) + return NULL; return filename; } +/* Calls dwarf2_directive_filename, but discards its result. + Used in pseudo-op tables where the function result is ignored. */ + +void +dwarf2_directive_file (int dummy ATTRIBUTE_UNUSED) +{ + (void) dwarf2_directive_filename (); +} + void dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) { @@ -619,10 +1108,14 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) if (filenum < 1) { - as_bad (_("file number less than one")); - return; + if (filenum != 0 || DWARF2_LINE_VERSION < 5) + { + as_bad (_("file number less than one")); + return; + } } - if (filenum >= (int) files_in_use || files[filenum].filename == 0) + + if (filenum >= (int) files_in_use || files[filenum].filename == NULL) { as_bad (_("unassigned file number %ld"), (long) filenum); return; @@ -721,6 +1214,57 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) return; } } + else if (strcmp (p, "view") == 0) + { + symbolS *sym; + + (void) restore_line_pointer (c); + SKIP_WHITESPACE (); + + if (ISDIGIT (*input_line_pointer) + || *input_line_pointer == '-') + { + bfd_boolean force_reset = *input_line_pointer == '-'; + + value = get_absolute_expression (); + if (value != 0) + { + as_bad (_("numeric view can only be asserted to zero")); + return; + } + if (force_reset && force_reset_view) + sym = force_reset_view; + else + { + sym = symbol_temp_new (absolute_section, value, + &zero_address_frag); + if (force_reset) + force_reset_view = sym; + } + } + else + { + char *name = read_symbol_name (); + + if (!name) + return; + sym = symbol_find_or_make (name); + if (S_IS_DEFINED (sym) || symbol_equated_p (sym)) + { + if (S_IS_VOLATILE (sym)) + sym = symbol_clone (sym, 1); + else if (!S_CAN_BE_REDEFINED (sym)) + { + as_bad (_("symbol `%s' is already defined"), name); + return; + } + } + S_SET_SEGMENT (sym, undefined_section); + S_SET_VALUE (sym, 0); + symbol_set_frag (sym, &zero_address_frag); + } + current.view = sym; + } else { as_bad (_("unknown .loc sub-directive `%s'"), p); @@ -734,6 +1278,10 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) demand_empty_rest_of_line (); dwarf2_loc_directive_seen = TRUE; debug_type = DEBUG_NONE; + + /* If we were given a view id, emit the row right away. */ + if (current.view) + dwarf2_emit_insn (0); } void @@ -854,6 +1402,7 @@ out_set_addr (symbolS *sym) { expressionS exp; + memset (&exp, 0, sizeof exp); out_opcode (DW_LNS_extended_op); out_uleb128 (sizeof_address + 1); @@ -873,10 +1422,10 @@ scale_addr_delta (addressT *addr_delta) if (DWARF2_LINE_MIN_INSN_LENGTH > 1) { if (*addr_delta % DWARF2_LINE_MIN_INSN_LENGTH != 0 && !printed_this) - { + { as_bad("unaligned opcodes detected in executable segment"); - printed_this = 1; - } + printed_this = 1; + } *addr_delta /= DWARF2_LINE_MIN_INSN_LENGTH; } } @@ -904,7 +1453,7 @@ size_inc_line_addr (int line_delta, addressT addr_delta) { if (addr_delta == MAX_SPECIAL_ADDR_DELTA) len = 1; - else + else if (addr_delta) len = 1 + sizeof_leb128 (addr_delta, 0); return len + 3; } @@ -968,7 +1517,7 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len) { if (addr_delta == MAX_SPECIAL_ADDR_DELTA) *p++ = DW_LNS_const_add_pc; - else + else if (addr_delta) { *p++ = DW_LNS_advance_pc; p += output_leb128 (p, addr_delta, 0); @@ -1119,6 +1668,7 @@ emit_fixed_inc_line_addr (int line_delta, addressT addr_delta, fragS *frag, symbolS *to_sym; expressionS exp; + memset (&exp, 0, sizeof exp); gas_assert (pexp->X_op == O_subtract); to_sym = pexp->X_add_symbol; @@ -1159,6 +1709,7 @@ relax_inc_line_addr (int line_delta, symbolS *to_sym, symbolS *from_sym) expressionS exp; int max_chars; + memset (&exp, 0, sizeof exp); exp.X_op = O_subtract; exp.X_add_symbol = to_sym; exp.X_op_symbol = from_sym; @@ -1292,7 +1843,7 @@ process_entries (segT seg, struct line_entry *e) that all of the sub-sections are merged into a proper .debug_line section before a debugger sees them. */ - sec_name = bfd_get_section_name (stdoutput, seg); + sec_name = bfd_section_name (seg); if (strcmp (sec_name, ".text") != 0) { name = concat (".debug_line", sec_name, (char *) NULL); @@ -1362,7 +1913,19 @@ process_entries (segT seg, struct line_entry *e) frag = symbol_get_frag (lab); frag_ofs = S_GET_VALUE (lab); - if (last_frag == NULL) + if (last_frag == NULL + || (e->loc.view == force_reset_view && force_reset_view + /* If we're going to reset the view, but we know we're + advancing the PC, we don't have to force with + set_address. We know we do when we're at the same + address of the same frag, and we know we might when + we're in the beginning of a frag, and we were at the + end of the previous frag. */ + && (frag == last_frag + ? (last_frag_ofs == frag_ofs) + : (frag_ofs == 0 + && ((offsetT)last_frag_ofs + >= get_frag_fix (last_frag, seg)))))) { out_set_addr (lab); out_inc_line_addr (line_delta, 0); @@ -1398,14 +1961,40 @@ process_entries (segT seg, struct line_entry *e) /* Emit the directory and file tables for .debug_line. */ static void -out_file_list (void) +out_dir_and_file_list (void) { size_t size; const char *dir; char *cp; unsigned int i; + bfd_boolean emit_md5 = FALSE; + bfd_boolean emit_timestamps = TRUE; + bfd_boolean emit_filesize = TRUE; + + /* Output the Directory Table. */ + if (DWARF2_LINE_VERSION >= 5) + { + out_byte (1); + out_uleb128 (DW_LNCT_path); + /* FIXME: it would be better to store these strings in + the .debug_line_str section and reference them here. */ + out_uleb128 (DW_FORM_string); + out_uleb128 (dirs_in_use); + } + /* Emit directory list. */ + if (DWARF2_LINE_VERSION >= 5) + { + if (dirs[0] == NULL) + dir = remap_debug_filename ("."); + else + dir = remap_debug_filename (dirs[0]); + + size = strlen (dir) + 1; + cp = frag_more (size); + memcpy (cp, dir, size); + } for (i = 1; i < dirs_in_use; ++i) { dir = remap_debug_filename (dirs[i]); @@ -1413,19 +2002,89 @@ out_file_list (void) cp = frag_more (size); memcpy (cp, dir, size); } - /* Terminate it. */ - out_byte ('\0'); - for (i = 1; i < files_in_use; ++i) + if (DWARF2_LINE_VERSION < 5) + /* Terminate it. */ + out_byte ('\0'); + + /* Output the File Name Table. */ + + if (DWARF2_LINE_VERSION >= 5) + { + unsigned int columns = 4; + + if (((unsigned long) DWARF2_FILE_TIME_NAME ("", "")) == -1UL) + { + emit_timestamps = FALSE; + -- columns; + } + + if (DWARF2_FILE_SIZE_NAME ("", "") == -1) + { + emit_filesize = FALSE; + -- columns; + } + + for (i = 0; i < files_in_use; ++i) + if (files[i].md5[0] != 0) + break; + if (i < files_in_use) + { + emit_md5 = TRUE; + ++ columns; + } + + /* The number of format entries to follow. */ + out_byte (columns); + + /* The format of the file name. */ + out_uleb128 (DW_LNCT_path); + /* FIXME: it would be better to store these strings in + the .debug_line_str section and reference them here. */ + out_uleb128 (DW_FORM_string); + + /* The format of the directory index. */ + out_uleb128 (DW_LNCT_directory_index); + out_uleb128 (DW_FORM_udata); + + if (emit_timestamps) + { + /* The format of the timestamp. */ + out_uleb128 (DW_LNCT_timestamp); + out_uleb128 (DW_FORM_udata); + } + + if (emit_filesize) + { + /* The format of the file size. */ + out_uleb128 (DW_LNCT_size); + out_uleb128 (DW_FORM_udata); + } + + if (emit_md5) + { + /* The format of the MD5 sum. */ + out_uleb128 (DW_LNCT_MD5); + out_uleb128 (DW_FORM_data16); + } + + /* The number of entries in the table. */ + out_uleb128 (files_in_use); + } + + for (i = DWARF2_LINE_VERSION > 4 ? 0 : 1; i < files_in_use; ++i) { const char *fullfilename; if (files[i].filename == NULL) { - as_bad (_("unassigned file number %ld"), (long) i); /* Prevent a crash later, particularly for file 1. */ files[i].filename = ""; - continue; + if (DWARF2_LINE_VERSION < 5 || i != 0) + { + as_bad (_("unassigned file number %ld"), (long) i); + continue; + } } fullfilename = DWARF2_FILE_NAME (files[i].filename, @@ -1434,17 +2093,45 @@ out_file_list (void) cp = frag_more (size); memcpy (cp, fullfilename, size); - out_uleb128 (files[i].dir); /* directory number */ + /* Directory number. */ + out_uleb128 (files[i].dir); + /* Output the last modification timestamp. */ - out_uleb128 (DWARF2_FILE_TIME_NAME (files[i].filename, - files[i].dir ? dirs [files [i].dir] : "")); + if (emit_timestamps) + { + offsetT timestamp; + + timestamp = DWARF2_FILE_TIME_NAME (files[i].filename, + files[i].dir ? dirs [files [i].dir] : ""); + if (timestamp == -1) + timestamp = 0; + out_uleb128 (timestamp); + } + /* Output the filesize. */ - out_uleb128 (DWARF2_FILE_SIZE_NAME (files[i].filename, - files[i].dir ? dirs [files [i].dir] : "")); + if (emit_filesize) + { + offsetT filesize; + filesize = DWARF2_FILE_SIZE_NAME (files[i].filename, + files[i].dir ? dirs [files [i].dir] : ""); + if (filesize == -1) + filesize = 0; + out_uleb128 (filesize); + } + + /* Output the md5 sum. */ + if (emit_md5) + { + int b; + + for (b = 0; b < NUM_MD5_BYTES; b++) + out_byte (files[i].md5[b]); + } } - /* Terminate filename list. */ - out_byte (0); + if (DWARF2_LINE_VERSION < 5) + /* Terminate filename list. */ + out_byte (0); } /* Switch to SEC and output a header length field. Return the size of @@ -1471,7 +2158,7 @@ out_header (asection *sec, expressionS *exp) } else { - start_sym = symbol_temp_new_now (); + start_sym = symbol_temp_new_now_octets (); end_sym = symbol_temp_make (); } @@ -1514,12 +2201,18 @@ out_debug_line (segT line_seg) struct line_seg *s; int sizeof_offset; + memset (&exp, 0, sizeof exp); sizeof_offset = out_header (line_seg, &exp); line_end = exp.X_add_symbol; /* Version. */ out_two (DWARF2_LINE_VERSION); + if (DWARF2_LINE_VERSION >= 5) + { + out_byte (sizeof_address); + out_byte (0); /* Segment Selector size. */ + } /* Length of the prologue following this length. */ prologue_start = symbol_temp_make (); prologue_end = symbol_temp_make (); @@ -1532,6 +2225,8 @@ out_debug_line (segT line_seg) /* Parameters of the state machine. */ out_byte (DWARF2_LINE_MIN_INSN_LENGTH); + if (DWARF2_LINE_VERSION >= 4) + out_byte (DWARF2_LINE_MAX_OPS_PER_INSN); out_byte (DWARF2_LINE_DEFAULT_IS_STMT); out_byte (DWARF2_LINE_BASE); out_byte (DWARF2_LINE_RANGE); @@ -1550,8 +2245,11 @@ out_debug_line (segT line_seg) out_byte (0); /* DW_LNS_set_prologue_end */ out_byte (0); /* DW_LNS_set_epilogue_begin */ out_byte (1); /* DW_LNS_set_isa */ + /* We have emitted 12 opcode lengths, so make that this + matches up to the opcode base value we have been using. */ + gas_assert (DWARF2_LINE_OPCODE_BASE == 13); - out_file_list (); + out_dir_and_file_list (); symbol_set_value_now (prologue_end); @@ -1584,6 +2282,7 @@ out_debug_ranges (segT ranges_seg) expressionS exp; unsigned int i; + memset (&exp, 0, sizeof exp); subseg_set (ranges_seg, 0); /* Base Address Entry. */ @@ -1637,6 +2336,7 @@ out_debug_aranges (segT aranges_seg, segT info_seg) char *p; int sizeof_offset; + memset (&exp, 0, sizeof exp); sizeof_offset = out_header (aranges_seg, &exp); aranges_end = exp.X_add_symbol; size = -exp.X_add_number; @@ -1746,6 +2446,7 @@ out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg, segT ranges_seg, symbolS *info_end; int sizeof_offset; + memset (&exp, 0, sizeof exp); sizeof_offset = out_header (info_seg, &exp); info_end = exp.X_add_symbol; @@ -1818,19 +2519,20 @@ out_debug_str (segT str_seg, symbolS **name_sym, symbolS **comp_dir_sym, const char *dirname; char *p; int len; + int first_file = DWARF2_LINE_VERSION > 4 ? 0 : 1; subseg_set (str_seg, 0); /* DW_AT_name. We don't have the actual file name that was present - on the command line, so assume files[1] is the main input file. + on the command line, so assume files[first_file] is the main input file. We're not supposed to get called unless at least one line number entry was emitted, so this should always be defined. */ - *name_sym = symbol_temp_new_now (); + *name_sym = symbol_temp_new_now_octets (); if (files_in_use == 0) abort (); - if (files[1].dir) + if (files[first_file].dir) { - dirname = remap_debug_filename (dirs[files[1].dir]); + dirname = remap_debug_filename (dirs[files[first_file].dir]); len = strlen (dirname); #ifdef TE_VMS /* Already has trailing slash. */ @@ -1842,19 +2544,19 @@ out_debug_str (segT str_seg, symbolS **name_sym, symbolS **comp_dir_sym, INSERT_DIR_SEPARATOR (p, len); #endif } - len = strlen (files[1].filename) + 1; + len = strlen (files[first_file].filename) + 1; p = frag_more (len); - memcpy (p, files[1].filename, len); + memcpy (p, files[first_file].filename, len); /* DW_AT_comp_dir */ - *comp_dir_sym = symbol_temp_new_now (); + *comp_dir_sym = symbol_temp_new_now_octets (); comp_dir = remap_debug_filename (getpwd ()); len = strlen (comp_dir) + 1; p = frag_more (len); memcpy (p, comp_dir, len); /* DW_AT_producer */ - *producer_sym = symbol_temp_new_now (); + *producer_sym = symbol_temp_new_now_octets (); sprintf (producer, "GNU AS %s", VERSION); len = strlen (producer) + 1; p = frag_more (len); @@ -1865,6 +2567,17 @@ void dwarf2_init (void) { last_seg_ptr = &all_segs; + + /* Select the default CIE version to produce here. The global + starts with a value of -1 and will be modified to a valid value + either by the user providing a command line option, or some + targets will select their own default in md_after_parse_args. If + we get here and the global still contains -1 then it is up to us + to pick a sane default. The default we choose is 1, this is the + CIE version gas has produced for a long time, and there seems no + reason to change it yet. */ + if (flag_dwarf_cie_version == -1) + flag_dwarf_cie_version = 1; } @@ -1909,7 +2622,7 @@ dwarf2_finish (void) /* Create and switch to the line number section. */ line_seg = subseg_new (".debug_line", 0); - bfd_set_section_flags (stdoutput, line_seg, SEC_READONLY | SEC_DEBUGGING); + bfd_set_section_flags (line_seg, SEC_READONLY | SEC_DEBUGGING | SEC_OCTETS); /* For each subsection, chain the debug entries together. */ for (s = all_segs; s; s = s->next) @@ -1917,8 +2630,19 @@ dwarf2_finish (void) struct line_subseg *lss = s->head; struct line_entry **ptail = lss->ptail; + /* Reset the initial view of the first subsection of the + section. */ + if (lss->head && lss->head->loc.view) + set_or_check_view (lss->head, NULL, NULL); + while ((lss = lss->next) != NULL) { + /* Link the first view of subsequent subsections to the + previous view. */ + if (lss->head && lss->head->loc.view) + set_or_check_view (lss->head, + !s->head ? NULL : (struct line_entry *)ptail, + s->head ? s->head->head : NULL); *ptail = lss->head; ptail = lss->ptail; } @@ -1944,15 +2668,15 @@ dwarf2_finish (void) aranges_seg = subseg_new (".debug_aranges", 0); str_seg = subseg_new (".debug_str", 0); - bfd_set_section_flags (stdoutput, info_seg, - SEC_READONLY | SEC_DEBUGGING); - bfd_set_section_flags (stdoutput, abbrev_seg, - SEC_READONLY | SEC_DEBUGGING); - bfd_set_section_flags (stdoutput, aranges_seg, - SEC_READONLY | SEC_DEBUGGING); - bfd_set_section_flags (stdoutput, str_seg, - (SEC_READONLY | SEC_DEBUGGING - | SEC_MERGE | SEC_STRINGS)); + bfd_set_section_flags (info_seg, + SEC_READONLY | SEC_DEBUGGING | SEC_OCTETS); + bfd_set_section_flags (abbrev_seg, + SEC_READONLY | SEC_DEBUGGING | SEC_OCTETS); + bfd_set_section_flags (aranges_seg, + SEC_READONLY | SEC_DEBUGGING | SEC_OCTETS); + bfd_set_section_flags (str_seg, + SEC_READONLY | SEC_DEBUGGING | SEC_OCTETS + | SEC_MERGE | SEC_STRINGS); str_seg->entsize = 1; record_alignment (aranges_seg, ffs (2 * sizeof_address) - 1); @@ -1962,8 +2686,8 @@ dwarf2_finish (void) else { ranges_seg = subseg_new (".debug_ranges", 0); - bfd_set_section_flags (stdoutput, ranges_seg, - SEC_READONLY | SEC_DEBUGGING); + bfd_set_section_flags (ranges_seg, + SEC_READONLY | SEC_DEBUGGING | SEC_OCTETS); record_alignment (ranges_seg, ffs (2 * sizeof_address) - 1); out_debug_ranges (ranges_seg); } @@ -1975,3 +2699,42 @@ dwarf2_finish (void) name_sym, comp_dir_sym, producer_sym); } } + +/* Perform any deferred checks pertaining to debug information. */ + +void +dwarf2dbg_final_check (void) +{ + /* Perform reset-view checks. Don't evaluate view_assert_failed + recursively: it could be very deep. It's a chain of adds, with + each chain element pointing to the next in X_add_symbol, and + holding the check value in X_op_symbol. */ + while (view_assert_failed) + { + expressionS *exp; + symbolS *sym; + offsetT failed; + + gas_assert (!symbol_resolved_p (view_assert_failed)); + + exp = symbol_get_value_expression (view_assert_failed); + sym = view_assert_failed; + + /* If view_assert_failed looks like a compound check in the + chain, break it up. */ + if (exp->X_op == O_add && exp->X_add_number == 0 && exp->X_unsigned) + { + view_assert_failed = exp->X_add_symbol; + sym = exp->X_op_symbol; + } + else + view_assert_failed = NULL; + + failed = resolve_symbol_value (sym); + if (!symbol_resolved_p (sym) || failed) + { + as_bad (_("view number mismatch")); + break; + } + } +}