X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gas%2Fdwarf2dbg.c;h=fd6ab54faa1f6fb355713ebb599d47b35091a124;hb=e97e2dcd466b43824713e85e307c287b34c8b4bc;hp=671a9b030d4b9932181049de03dc0291d2e0e7af;hpb=eb1fe072ef479ea99f4a5a1ab11d9271bf3078c1;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/dwarf2dbg.c b/gas/dwarf2dbg.c index 671a9b030d..fd6ab54faa 100644 --- a/gas/dwarf2dbg.c +++ b/gas/dwarf2dbg.c @@ -1,13 +1,12 @@ /* dwarf2dbg.c - DWARF2 debug support - Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. + Copyright (C) 1999-2019 Free Software Foundation, Inc. Contributed by David Mosberger-Tang This file is part of GAS, the GNU Assembler. GAS 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 2, or (at your option) + the Free Software Foundation; either version 3, or (at your option) any later version. GAS is distributed in the hope that it will be useful, @@ -25,7 +24,8 @@ .file FILENO "file.c" .loc FILENO LINENO [COLUMN] [basic_block] [prologue_end] \ - [epilogue_begin] [is_stmt VALUE] [isa VALUE] + [epilogue_begin] [is_stmt VALUE] [isa VALUE] \ + [discriminator VALUE] */ #include "as.h" @@ -57,8 +57,8 @@ do \ { \ if (offset > 1 \ - && string[0] != 0 \ - && string[1] == ':') \ + && string[0] != 0 \ + && string[1] == ':') \ string [offset] = '\\'; \ else \ string [offset] = '/'; \ @@ -69,16 +69,42 @@ #endif #ifndef DWARF2_FORMAT -# define DWARF2_FORMAT() dwarf2_format_32bit +# define DWARF2_FORMAT(SEC) dwarf2_format_32bit #endif #ifndef DWARF2_ADDR_SIZE # define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8) #endif +#ifndef DWARF2_FILE_NAME +#define DWARF2_FILE_NAME(FILENAME, DIRNAME) FILENAME +#endif + +#ifndef DWARF2_FILE_TIME_NAME +#define DWARF2_FILE_TIME_NAME(FILENAME,DIRNAME) 0 +#endif + +#ifndef DWARF2_FILE_SIZE_NAME +#define DWARF2_FILE_SIZE_NAME(FILENAME,DIRNAME) 0 +#endif + +#ifndef DWARF2_VERSION +#define DWARF2_VERSION 2 +#endif + +/* The .debug_aranges version has been 2 in DWARF version 2, 3 and 4. */ +#ifndef DWARF2_ARANGES_VERSION +#define DWARF2_ARANGES_VERSION 2 +#endif + +/* This implementation outputs version 3 .debug_line information. */ +#ifndef DWARF2_LINE_VERSION +#define DWARF2_LINE_VERSION 3 +#endif + #include "subsegs.h" -#include "elf/dwarf2.h" +#include "dwarf2.h" /* Since we can't generate the prolog until the body is complete, we use three different subsegments for .debug_line: one holding the @@ -92,10 +118,10 @@ opcodes and variable-length operands cannot be used. If this macro is nonzero, use the DW_LNS_fixed_advance_pc opcode instead. */ #ifndef DWARF2_USE_FIXED_ADVANCE_PC -# define DWARF2_USE_FIXED_ADVANCE_PC 0 +# define DWARF2_USE_FIXED_ADVANCE_PC linkrelax #endif -/* First special line opcde - leave room for the standard opcodes. +/* First special line opcode - leave room for the standard opcodes. Note: If you want to change this, you'll have to update the "standard_opcode_lengths" table that is emitted below in out_debug_line(). */ @@ -132,20 +158,33 @@ /* The maximum address skip amount that can be encoded with a special op. */ #define MAX_SPECIAL_ADDR_DELTA SPECIAL_ADDR(255) -struct line_entry { +#ifndef TC_PARSE_CONS_RETURN_NONE +#define TC_PARSE_CONS_RETURN_NONE BFD_RELOC_NONE +#endif + +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; struct line_entry **ptail; + struct line_entry **pmove_tail; }; -struct line_seg { +struct line_seg +{ struct line_seg *next; segT seg; struct line_subseg *head; @@ -155,8 +194,10 @@ struct line_seg { /* Collects data for all line table entries during assembly. */ static struct line_seg *all_segs; +static struct line_seg **last_seg_ptr; -struct file_entry { +struct file_entry +{ const char *filename; unsigned int dir; }; @@ -173,45 +214,33 @@ static unsigned int dirs_allocated; /* TRUE when we've seen a .loc directive recently. Used to avoid doing work when there's nothing to do. */ -static bfd_boolean loc_directive_seen; +bfd_boolean dwarf2_loc_directive_seen; /* TRUE when we're supposed to set the basic block mark whenever a label is 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 + DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 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 struct line_subseg *get_line_subseg (segT, subsegT); static unsigned int get_filenum (const char *, unsigned int); -static struct frag *first_frag_for_seg (segT); -static struct frag *last_frag_for_seg (segT); -static void out_byte (int); -static void out_opcode (int); -static void out_two (int); -static void out_four (int); -static void out_abbrev (int, int); -static void out_uleb128 (addressT); -static void out_sleb128 (addressT); -static offsetT get_frag_fix (fragS *, segT); -static void out_set_addr (symbolS *); -static int size_inc_line_addr (int, addressT); -static void emit_inc_line_addr (int, addressT, char *, int); -static void out_inc_line_addr (int, addressT); -static void out_fixed_inc_line_addr (int, symbolS *, symbolS *); -static void relax_inc_line_addr (int, symbolS *, symbolS *); -static void process_entries (segT, struct line_entry *); -static void out_file_list (void); -static void out_debug_line (segT); -static void out_debug_aranges (segT, segT); -static void out_debug_abbrev (segT); - + #ifndef TC_DWARF2_EMIT_OFFSET #define TC_DWARF2_EMIT_OFFSET generic_dwarf2_emit_offset @@ -220,62 +249,234 @@ static void out_debug_abbrev (segT); static void generic_dwarf2_emit_offset (symbolS *symbol, unsigned int size) { - expressionS expr; + expressionS exp; - expr.X_op = O_symbol; - expr.X_add_symbol = symbol; - expr.X_add_number = 0; - emit_expr (&expr, size); + memset (&exp, 0, sizeof exp); + exp.X_op = O_symbol; + exp.X_add_symbol = symbol; + exp.X_add_number = 0; + emit_expr (&exp, size); } #endif -/* Find or create an entry for SEG+SUBSEG in ALL_SEGS. */ +/* Find or create (if CREATE_P) an entry for SEG+SUBSEG in ALL_SEGS. */ static struct line_subseg * -get_line_subseg (segT seg, subsegT subseg) +get_line_subseg (segT seg, subsegT subseg, bfd_boolean create_p) { - static segT last_seg; - static subsegT last_subseg; - static struct line_subseg *last_line_subseg; - - struct line_seg **ps, *s; - struct line_subseg **pss, *ss; + struct line_seg *s = seg_info (seg)->dwarf2_line_seg; + struct line_subseg **pss, *lss; - if (seg == last_seg && subseg == last_subseg) - return last_line_subseg; - - for (ps = &all_segs; (s = *ps) != NULL; ps = &s->next) - if (s->seg == seg) - goto found_seg; - - s = (struct line_seg *) xmalloc (sizeof (*s)); - s->next = NULL; - s->seg = seg; - s->head = NULL; - *ps = s; + if (s == NULL) + { + if (!create_p) + return NULL; + + s = XNEW (struct line_seg); + s->next = NULL; + s->seg = seg; + s->head = NULL; + *last_seg_ptr = s; + last_seg_ptr = &s->next; + seg_info (seg)->dwarf2_line_seg = s; + } + gas_assert (seg == s->seg); - found_seg: - for (pss = &s->head; (ss = *pss) != NULL ; pss = &ss->next) + for (pss = &s->head; (lss = *pss) != NULL ; pss = &lss->next) { - if (ss->subseg == subseg) + if (lss->subseg == subseg) goto found_subseg; - if (ss->subseg > subseg) + if (lss->subseg > subseg) break; } - ss = (struct line_subseg *) xmalloc (sizeof (*ss)); - ss->next = *pss; - ss->subseg = subseg; - ss->head = NULL; - ss->ptail = &ss->head; - *pss = ss; + lss = XNEW (struct line_subseg); + lss->next = *pss; + lss->subseg = subseg; + lss->head = NULL; + lss->ptail = &lss->head; + lss->pmove_tail = &lss->head; + *pss = lss; found_subseg: - last_seg = seg; - last_subseg = subseg; - last_line_subseg = ss; + 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; - return ss; + 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. */ @@ -283,17 +484,25 @@ get_line_subseg (segT seg, subsegT subseg) static void dwarf2_gen_line_info_1 (symbolS *label, struct dwarf2_line_info *loc) { - struct line_subseg *ss; + struct line_subseg *lss; struct line_entry *e; - e = (struct line_entry *) xmalloc (sizeof (*e)); + e = XNEW (struct line_entry); e->next = NULL; e->label = label; e->loc = *loc; - ss = get_line_subseg (now_seg, now_subseg); - *ss->ptail = e; - ss->ptail = &e->next; + 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; } /* Record an entry for LOC occurring at OFS within the current fragment. */ @@ -321,7 +530,17 @@ dwarf2_gen_line_info (addressT ofs, struct dwarf2_line_info *loc) line = loc->line; filenum = loc->filenum; - sym = symbol_temp_new (now_seg, ofs, frag_now); + if (linkrelax) + { + char name[120]; + + /* Use a non-fake name for the line number location, + so that it can be referred to by relocations. */ + sprintf (name, ".Loc.%u.%u", line, filenum); + sym = symbol_new (name, now_seg, ofs, frag_now); + } + else + sym = symbol_temp_new (now_seg, ofs, frag_now); dwarf2_gen_line_info_1 (sym, loc); } @@ -335,18 +554,22 @@ dwarf2_where (struct dwarf2_line_info *line) { if (debug_type == DEBUG_DWARF2) { - char *filename; - as_where (&filename, &line->line); + const char *filename; + + memset (line, 0, sizeof (*line)); + filename = as_where (&line->line); line->filenum = get_filenum (filename, 0); line->column = 0; line->flags = DWARF2_FLAG_IS_STMT; line->isa = current.isa; + line->discriminator = current.discriminator; + line->view = NULL; } else *line = current; } -/* A hook to allow the target backend to inform the line number state +/* A hook to allow the target backend to inform the line number state machine of isa changes when assembler debug info is enabled. */ void @@ -365,30 +588,61 @@ dwarf2_emit_insn (int size) { struct dwarf2_line_info loc; - if (loc_directive_seen) + 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_octets () - size) / OCTETS_PER_BYTE, &loc); + dwarf2_consume_line_info (); +} + +/* Move all previously-emitted line entries for the current position by + DELTA bytes. This function cannot be used to move the same entries + twice. */ + +void +dwarf2_move_insn (int delta) +{ + struct line_subseg *lss; + struct line_entry *e; + valueT now; + + if (delta == 0) + return; + + lss = get_line_subseg (now_seg, now_subseg, FALSE); + if (!lss) + return; + + now = frag_now_fix (); + while ((e = *lss->pmove_tail)) { - /* Use the last location established by a .loc directive, not - the value returned by dwarf2_where(). That calls as_where() - which will return either the logical input file name (foo.c) - or the physical input file name (foo.s) and not the file name - specified in the most recent .loc directive (eg foo.h). */ - loc = current; - - /* Unless we generate DWARF2 debugging information for each - assembler line, we only emit one line symbol for one LOC. */ - if (debug_type != DEBUG_DWARF2) - loc_directive_seen = FALSE; + if (S_GET_VALUE (e->label) == now) + S_SET_VALUE (e->label, now + delta); + lss->pmove_tail = &e->next; } - else if (debug_type != DEBUG_DWARF2) - return; - else - dwarf2_where (&loc); +} + +/* Called after the current line information has been either used with + dwarf2_gen_line_info or saved with a machine instruction for later use. + This resets the state of the line number information to reflect that + it has been used. */ - dwarf2_gen_line_info (frag_now_fix () - size, &loc); +void +dwarf2_consume_line_info (void) +{ + /* Unless we generate DWARF2 debugging information for each + assembler line, we only emit one line symbol for one LOC. */ + dwarf2_loc_directive_seen = FALSE; current.flags &= ~(DWARF2_FLAG_BASIC_BLOCK | 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 @@ -403,24 +657,17 @@ 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; - - if (debug_type == DEBUG_DWARF2) - dwarf2_where (&loc); - else - { - loc = current; - loc_directive_seen = FALSE; - } - loc.flags |= DWARF2_FLAG_BASIC_BLOCK; + dwarf2_where (&loc); - current.flags &= ~(DWARF2_FLAG_BASIC_BLOCK - | DWARF2_FLAG_PROLOGUE_END - | DWARF2_FLAG_EPILOGUE_BEGIN); + loc.flags |= DWARF2_FLAG_BASIC_BLOCK; dwarf2_gen_line_info_1 (label, &loc); + dwarf2_consume_line_info (); } /* Get a .debug_line file number for FILENAME. If NUM is nonzero, @@ -438,14 +685,14 @@ get_filenum (const char *filename, unsigned int num) if (num == 0 && last_used) { if (! files[last_used].dir - && strcmp (filename, files[last_used].filename) == 0) + && filename_cmp (filename, files[last_used].filename) == 0) return last_used; if (files[last_used].dir - && strncmp (filename, dirs[files[last_used].dir], - last_used_dir_len) == 0 + && filename_ncmp (filename, dirs[files[last_used].dir], + last_used_dir_len) == 0 && IS_DIR_SEPARATOR (filename [last_used_dir_len]) - && strcmp (filename + last_used_dir_len + 1, - files[last_used].filename) == 0) + && filename_cmp (filename + last_used_dir_len + 1, + files[last_used].filename) == 0) return last_used; } @@ -463,9 +710,11 @@ get_filenum (const char *filename, unsigned int num) dir = 0; if (dir_len) { +#ifndef DWARF2_DIR_SHOULD_END_WITH_SEPARATOR --dir_len; +#endif for (dir = 1; dir < dirs_in_use; ++dir) - if (strncmp (filename, dirs[dir], dir_len) == 0 + if (filename_ncmp (filename, dirs[dir], dir_len) == 0 && dirs[dir][dir_len] == '\0') break; @@ -474,13 +723,10 @@ get_filenum (const char *filename, unsigned int num) if (dir >= dirs_allocated) { dirs_allocated = dir + 32; - dirs = (char **) - xrealloc (dirs, (dir + 32) * sizeof (const char *)); + dirs = XRESIZEVEC (char *, dirs, dirs_allocated); } - dirs[dir] = xmalloc (dir_len + 1); - memcpy (dirs[dir], filename, dir_len); - dirs[dir][dir_len] = '\0'; + dirs[dir] = xmemdup0 (filename, dir_len); dirs_in_use = dir + 1; } } @@ -490,7 +736,7 @@ get_filenum (const char *filename, unsigned int num) for (i = 1; i < files_in_use; ++i) if (files[i].dir == dir && files[i].filename - && strcmp (file, files[i].filename) == 0) + && filename_cmp (file, files[i].filename) == 0) { last_used = i; last_used_dir_len = dir_len; @@ -505,13 +751,18 @@ get_filenum (const char *filename, unsigned int num) unsigned int old = files_allocated; files_allocated = i + 32; - files = (struct file_entry *) - xrealloc (files, (i + 32) * sizeof (struct file_entry)); + /* Catch wraparound. */ + if (files_allocated <= old) + { + as_bad (_("file number %lu is too big"), (unsigned long) i); + return 0; + } + 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].filename = file; files[i].dir = dir; if (files_in_use < i + 1) files_in_use = i + 1; @@ -525,12 +776,12 @@ get_filenum (const char *filename, unsigned int num) - 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; + valueT num; char *filename; int filename_len; @@ -548,28 +799,52 @@ dwarf2_directive_file (int dummy ATTRIBUTE_UNUSED) return NULL; demand_empty_rest_of_line (); - if (num < 1) + if ((offsetT) num < 1) { as_bad (_("file number less than one")); return NULL; } - if (num < (int) files_in_use && files[num].filename != 0) + /* A .file directive implies compiler generated debug information is + being supplied. Turn off gas generated debug info. */ + debug_type = DEBUG_NONE; + + 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; + } + if (num < files_in_use && files[num].filename != 0) + { + as_bad (_("file number %u already allocated"), (unsigned int) num); return NULL; } - get_filenum (filename, num); + get_filenum (filename, (unsigned int) num); 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) { offsetT filenum, line; + /* If we see two .loc directives in a row, force the first one to be + output now. */ + if (dwarf2_loc_directive_seen) + dwarf2_emit_insn (0); + filenum = get_absolute_expression (); SKIP_WHITESPACE (); line = get_absolute_expression (); @@ -587,6 +862,7 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) current.filenum = filenum; current.line = line; + current.discriminator = 0; #ifndef NO_LISTING if (listing) @@ -595,13 +871,14 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) { size_t dir_len = strlen (dirs[files[filenum].dir]); size_t file_len = strlen (files[filenum].filename); - char *cp = (char *) alloca (dir_len + 1 + file_len + 1); + char *cp = XNEWVEC (char, dir_len + 1 + file_len + 1); memcpy (cp, dirs[files[filenum].dir], dir_len); INSERT_DIR_SEPARATOR (cp, dir_len); memcpy (cp + dir_len + 1, files[filenum].filename, file_len); cp[dir_len + file_len + 1] = '\0'; listing_source_file (cp); + free (cp); } else listing_source_file (files[filenum].filename); @@ -621,8 +898,7 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) char *p, c; offsetT value; - p = input_line_pointer; - c = get_symbol_end (); + c = get_symbol_name (& p); if (strcmp (p, "basic_block") == 0) { @@ -641,7 +917,7 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) } else if (strcmp (p, "is_stmt") == 0) { - *input_line_pointer = c; + (void) restore_line_pointer (c); value = get_absolute_expression (); if (value == 0) current.flags &= ~DWARF2_FLAG_IS_STMT; @@ -655,7 +931,7 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) } else if (strcmp (p, "isa") == 0) { - *input_line_pointer = c; + (void) restore_line_pointer (c); value = get_absolute_expression (); if (value >= 0) current.isa = value; @@ -665,18 +941,86 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) return; } } + else if (strcmp (p, "discriminator") == 0) + { + (void) restore_line_pointer (c); + value = get_absolute_expression (); + if (value >= 0) + current.discriminator = value; + else + { + as_bad (_("discriminator less than zero")); + 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); - *input_line_pointer = c; + (void) restore_line_pointer (c); return; } - SKIP_WHITESPACE (); + SKIP_WHITESPACE_AFTER_NAME (); } demand_empty_rest_of_line (); - loc_directive_seen = TRUE; + 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 @@ -756,7 +1100,7 @@ out_uleb128 (addressT value) /* Emit a signed "little-endian base 128" number. */ static void -out_sleb128 (addressT value) +out_leb128 (addressT value) { output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1); } @@ -795,36 +1139,35 @@ get_frag_fix (fragS *frag, segT seg) static void out_set_addr (symbolS *sym) { - expressionS expr; + expressionS exp; + memset (&exp, 0, sizeof exp); out_opcode (DW_LNS_extended_op); out_uleb128 (sizeof_address + 1); out_opcode (DW_LNE_set_address); - expr.X_op = O_symbol; - expr.X_add_symbol = sym; - expr.X_add_number = 0; - emit_expr (&expr, sizeof_address); + exp.X_op = O_symbol; + exp.X_add_symbol = sym; + exp.X_add_number = 0; + emit_expr (&exp, sizeof_address); } -#if DWARF2_LINE_MIN_INSN_LENGTH > 1 static void scale_addr_delta (addressT *); static void scale_addr_delta (addressT *addr_delta) { static int printed_this = 0; - if (*addr_delta % DWARF2_LINE_MIN_INSN_LENGTH != 0) + if (DWARF2_LINE_MIN_INSN_LENGTH > 1) { - if (!printed_this) - as_bad("unaligned opcodes detected in executable segment"); - printed_this = 1; + if (*addr_delta % DWARF2_LINE_MIN_INSN_LENGTH != 0 && !printed_this) + { + as_bad("unaligned opcodes detected in executable segment"); + printed_this = 1; + } + *addr_delta /= DWARF2_LINE_MIN_INSN_LENGTH; } - *addr_delta /= DWARF2_LINE_MIN_INSN_LENGTH; } -#else -#define scale_addr_delta(A) -#endif /* Encode a pair of line and address skips as efficiently as possible. Note that the line skip is signed, whereas the address skip is unsigned. @@ -849,7 +1192,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; } @@ -901,7 +1244,7 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len) /* Line number sequences cannot go backward in addresses. This means we've incorrectly ordered the statements in the sequence. */ - assert ((offsetT) addr_delta >= 0); + gas_assert ((offsetT) addr_delta >= 0); /* Scale the address delta by the minimum instruction length. */ scale_addr_delta (&addr_delta); @@ -913,7 +1256,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); @@ -982,7 +1325,7 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len) *p++ = tmp; done: - assert (p == end); + gas_assert (p == end); } /* Handy routine to combine calls to the above two routines. */ @@ -996,41 +1339,104 @@ out_inc_line_addr (int line_delta, addressT addr_delta) /* Write out an alternative form of line and address skips using DW_LNS_fixed_advance_pc opcodes. This uses more space than the default - line and address information, but it helps support linker relaxation that - changes the code offsets. */ + line and address information, but it is required if linker relaxation + could change the code offsets. The following two routines *must* be + kept in sync. */ +#define ADDR_DELTA_LIMIT 50000 -static void -out_fixed_inc_line_addr (int line_delta, symbolS *to_sym, symbolS *from_sym) +static int +size_fixed_inc_line_addr (int line_delta, addressT addr_delta) { - expressionS expr; + int len = 0; /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence. */ + if (line_delta != INT_MAX) + len = 1 + sizeof_leb128 (line_delta, 1); + + if (addr_delta > ADDR_DELTA_LIMIT) + { + /* DW_LNS_extended_op */ + len += 1 + sizeof_leb128 (sizeof_address + 1, 0); + /* DW_LNE_set_address */ + len += 1 + sizeof_address; + } + else + /* DW_LNS_fixed_advance_pc */ + len += 3; + if (line_delta == INT_MAX) + /* DW_LNS_extended_op + DW_LNE_end_sequence */ + len += 3; + else + /* DW_LNS_copy */ + len += 1; + + return len; +} + +static void +emit_fixed_inc_line_addr (int line_delta, addressT addr_delta, fragS *frag, + char *p, int len) +{ + expressionS *pexp; + char *end = p + len; + + /* Line number sequences cannot go backward in addresses. This means + we've incorrectly ordered the statements in the sequence. */ + gas_assert ((offsetT) addr_delta >= 0); + + /* Verify that we have kept in sync with size_fixed_inc_line_addr. */ + gas_assert (len == size_fixed_inc_line_addr (line_delta, addr_delta)); + + /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence. */ + if (line_delta != INT_MAX) { - out_opcode (DW_LNS_fixed_advance_pc); - expr.X_op = O_subtract; - expr.X_add_symbol = to_sym; - expr.X_op_symbol = from_sym; - expr.X_add_number = 0; - emit_expr (&expr, 2); - - out_opcode (DW_LNS_extended_op); - out_byte (1); - out_opcode (DW_LNE_end_sequence); - return; + *p++ = DW_LNS_advance_line; + p += output_leb128 (p, line_delta, 1); } - out_opcode (DW_LNS_advance_line); - out_sleb128 (line_delta); + pexp = symbol_get_value_expression (frag->fr_symbol); + + /* The DW_LNS_fixed_advance_pc opcode has a 2-byte operand so it can + advance the address by at most 64K. Linker relaxation (without + which this function would not be used) could change the operand by + an unknown amount. If the address increment is getting close to + the limit, just reset the address. */ + if (addr_delta > ADDR_DELTA_LIMIT) + { + symbolS *to_sym; + expressionS exp; - out_opcode (DW_LNS_fixed_advance_pc); - expr.X_op = O_subtract; - expr.X_add_symbol = to_sym; - expr.X_op_symbol = from_sym; - expr.X_add_number = 0; - emit_expr (&expr, 2); + memset (&exp, 0, sizeof exp); + gas_assert (pexp->X_op == O_subtract); + to_sym = pexp->X_add_symbol; - out_opcode (DW_LNS_copy); + *p++ = DW_LNS_extended_op; + p += output_leb128 (p, sizeof_address + 1, 0); + *p++ = DW_LNE_set_address; + exp.X_op = O_symbol; + exp.X_add_symbol = to_sym; + exp.X_add_number = 0; + emit_expr_fix (&exp, sizeof_address, frag, p, TC_PARSE_CONS_RETURN_NONE); + p += sizeof_address; + } + else + { + *p++ = DW_LNS_fixed_advance_pc; + emit_expr_fix (pexp, 2, frag, p, TC_PARSE_CONS_RETURN_NONE); + p += 2; + } + + if (line_delta == INT_MAX) + { + *p++ = DW_LNS_extended_op; + *p++ = 1; + *p++ = DW_LNE_end_sequence; + } + else + *p++ = DW_LNS_copy; + + gas_assert (p == end); } /* Generate a variant frag that we can use to relax address/line @@ -1039,20 +1445,25 @@ out_fixed_inc_line_addr (int line_delta, symbolS *to_sym, symbolS *from_sym) static void relax_inc_line_addr (int line_delta, symbolS *to_sym, symbolS *from_sym) { - expressionS expr; + expressionS exp; int max_chars; - expr.X_op = O_subtract; - expr.X_add_symbol = to_sym; - expr.X_op_symbol = from_sym; - expr.X_add_number = 0; + memset (&exp, 0, sizeof exp); + exp.X_op = O_subtract; + exp.X_add_symbol = to_sym; + exp.X_op_symbol = from_sym; + exp.X_add_number = 0; /* The maximum size of the frag is the line delta with a maximum sized address delta. */ - max_chars = size_inc_line_addr (line_delta, -DWARF2_LINE_MIN_INSN_LENGTH); + if (DWARF2_USE_FIXED_ADVANCE_PC) + max_chars = size_fixed_inc_line_addr (line_delta, + -DWARF2_LINE_MIN_INSN_LENGTH); + else + max_chars = size_inc_line_addr (line_delta, -DWARF2_LINE_MIN_INSN_LENGTH); frag_var (rs_dwarf2dbg, max_chars, max_chars, 1, - make_expr_symbol (&expr), line_delta, NULL); + make_expr_symbol (&exp), line_delta, NULL); } /* The function estimates the size of a rs_dwarf2dbg variant frag @@ -1066,7 +1477,10 @@ dwarf2dbg_estimate_size_before_relax (fragS *frag) int size; addr_delta = resolve_symbol_value (frag->fr_symbol); - size = size_inc_line_addr (frag->fr_offset, addr_delta); + if (DWARF2_USE_FIXED_ADVANCE_PC) + size = size_fixed_inc_line_addr (frag->fr_offset, addr_delta); + else + size = size_inc_line_addr (frag->fr_offset, addr_delta); frag->fr_subtype = size; @@ -1097,15 +1511,37 @@ dwarf2dbg_convert_frag (fragS *frag) { offsetT addr_diff; - addr_diff = resolve_symbol_value (frag->fr_symbol); + if (DWARF2_USE_FIXED_ADVANCE_PC) + { + /* If linker relaxation is enabled then the distance between the two + symbols in the frag->fr_symbol expression might change. Hence we + cannot rely upon the value computed by resolve_symbol_value. + Instead we leave the expression unfinalized and allow + emit_fixed_inc_line_addr to create a fixup (which later becomes a + relocation) that will allow the linker to correctly compute the + actual address difference. We have to use a fixed line advance for + this as we cannot (easily) relocate leb128 encoded values. */ + int saved_finalize_syms = finalize_syms; + + finalize_syms = 0; + addr_diff = resolve_symbol_value (frag->fr_symbol); + finalize_syms = saved_finalize_syms; + } + else + addr_diff = resolve_symbol_value (frag->fr_symbol); /* fr_var carries the max_chars that we created the fragment with. fr_subtype carries the current expected length. We must, of course, have allocated enough memory earlier. */ - assert (frag->fr_var >= (int) frag->fr_subtype); + gas_assert (frag->fr_var >= (int) frag->fr_subtype); - emit_inc_line_addr (frag->fr_offset, addr_diff, - frag->fr_literal + frag->fr_fix, frag->fr_subtype); + if (DWARF2_USE_FIXED_ADVANCE_PC) + emit_fixed_inc_line_addr (frag->fr_offset, addr_diff, frag, + frag->fr_literal + frag->fr_fix, + frag->fr_subtype); + else + emit_inc_line_addr (frag->fr_offset, addr_diff, + frag->fr_literal + frag->fr_fix, frag->fr_subtype); frag->fr_fix += frag->fr_subtype; frag->fr_type = rs_fill; @@ -1129,6 +1565,36 @@ process_entries (segT seg, struct line_entry *e) symbolS *last_lab = NULL, *lab; struct line_entry *next; + if (flag_dwarf_sections) + { + char * name; + const char * sec_name; + + /* Switch to the relevant sub-section before we start to emit + the line number table. + + FIXME: These sub-sections do not have a normal Line Number + Program Header, thus strictly speaking they are not valid + DWARF sections. Unfortunately the DWARF standard assumes + a one-to-one relationship between compilation units and + line number tables. Thus we have to have a .debug_line + section, as well as our sub-sections, and we have to ensure + that all of the sub-sections are merged into a proper + .debug_line section before a debugger sees them. */ + + sec_name = bfd_section_name (seg); + if (strcmp (sec_name, ".text") != 0) + { + name = concat (".debug_line", sec_name, (char *) NULL); + subseg_set (subseg_get (name, FALSE), 0); + } + else + /* Don't create a .debug_line.text section - + that is redundant. Instead just switch back to the + normal .debug_line section. */ + subseg_set (subseg_get (".debug_line", FALSE), 0); + } + do { int line_delta; @@ -1147,6 +1613,14 @@ process_entries (segT seg, struct line_entry *e) out_uleb128 (column); } + if (e->loc.discriminator != 0) + { + out_opcode (DW_LNS_extended_op); + out_leb128 (1 + sizeof_leb128 (e->loc.discriminator, 0)); + out_opcode (DW_LNE_set_discriminator); + out_uleb128 (e->loc.discriminator); + } + if (isa != e->loc.isa) { isa = e->loc.isa; @@ -1178,14 +1652,24 @@ 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); } - else if (DWARF2_USE_FIXED_ADVANCE_PC) - out_fixed_inc_line_addr (line_delta, lab, last_lab); - else if (frag == last_frag) + else if (frag == last_frag && ! DWARF2_USE_FIXED_ADVANCE_PC) out_inc_line_addr (line_delta, frag_ofs - last_frag_ofs); else relax_inc_line_addr (line_delta, lab, last_lab); @@ -1204,12 +1688,7 @@ process_entries (segT seg, struct line_entry *e) /* Emit a DW_LNE_end_sequence for the end of the section. */ frag = last_frag_for_seg (seg); frag_ofs = get_frag_fix (frag, seg); - if (DWARF2_USE_FIXED_ADVANCE_PC) - { - lab = symbol_temp_new (seg, frag_ofs, frag); - out_fixed_inc_line_addr (INT_MAX, lab, last_lab); - } - else if (frag == last_frag) + if (frag == last_frag && ! DWARF2_USE_FIXED_ADVANCE_PC) out_inc_line_addr (INT_MAX, frag_ofs - last_frag_ofs); else { @@ -1224,21 +1703,25 @@ static void out_file_list (void) { size_t size; + const char *dir; char *cp; unsigned int i; /* Emit directory list. */ for (i = 1; i < dirs_in_use; ++i) { - size = strlen (dirs[i]) + 1; + dir = remap_debug_filename (dirs[i]); + size = strlen (dir) + 1; cp = frag_more (size); - memcpy (cp, dirs[i], size); + memcpy (cp, dir, size); } /* Terminate it. */ out_byte ('\0'); for (i = 1; i < files_in_use; ++i) { + const char *fullfilename; + if (files[i].filename == NULL) { as_bad (_("unassigned file number %ld"), (long) i); @@ -1247,77 +1730,108 @@ out_file_list (void) continue; } - size = strlen (files[i].filename) + 1; + fullfilename = DWARF2_FILE_NAME (files[i].filename, + files[i].dir ? dirs [files [i].dir] : ""); + size = strlen (fullfilename) + 1; cp = frag_more (size); - memcpy (cp, files[i].filename, size); + memcpy (cp, fullfilename, size); out_uleb128 (files[i].dir); /* directory number */ - out_uleb128 (0); /* last modification timestamp */ - out_uleb128 (0); /* filesize */ + /* Output the last modification timestamp. */ + out_uleb128 (DWARF2_FILE_TIME_NAME (files[i].filename, + files[i].dir ? dirs [files [i].dir] : "")); + /* Output the filesize. */ + out_uleb128 (DWARF2_FILE_SIZE_NAME (files[i].filename, + files[i].dir ? dirs [files [i].dir] : "")); } /* Terminate filename list. */ out_byte (0); } -/* Emit the collected .debug_line data. */ +/* Switch to SEC and output a header length field. Return the size of + offsets used in SEC. The caller must set EXPR->X_add_symbol value + to the end of the section. EXPR->X_add_number will be set to the + negative size of the header. */ -static void -out_debug_line (segT line_seg) +static int +out_header (asection *sec, expressionS *exp) { - expressionS expr; - symbolS *line_start; - symbolS *prologue_end; - symbolS *line_end; - struct line_seg *s; - enum dwarf2_format d2f; - int sizeof_offset; - - subseg_set (line_seg, 0); + symbolS *start_sym; + symbolS *end_sym; - line_start = symbol_temp_new_now (); - prologue_end = symbol_temp_make (); - line_end = symbol_temp_make (); - - /* Total length of the information for this compilation unit. */ - expr.X_op = O_subtract; - expr.X_add_symbol = line_end; - expr.X_op_symbol = line_start; + subseg_set (sec, 0); - d2f = DWARF2_FORMAT (); - if (d2f == dwarf2_format_32bit) + if (flag_dwarf_sections) { - expr.X_add_number = -4; - emit_expr (&expr, 4); - sizeof_offset = 4; + /* If we are going to put the start and end symbols in different + sections, then we need real symbols, not just fake, local ones. */ + frag_now_fix (); + start_sym = symbol_make (".Ldebug_line_start"); + end_sym = symbol_make (".Ldebug_line_end"); + symbol_set_value_now (start_sym); } - else if (d2f == dwarf2_format_64bit) - { - expr.X_add_number = -12; - out_four (-1); - emit_expr (&expr, 8); - sizeof_offset = 8; - } - else if (d2f == dwarf2_format_64bit_irix) + else { - expr.X_add_number = -8; - emit_expr (&expr, 8); - sizeof_offset = 8; + start_sym = symbol_temp_new_now_octets (); + end_sym = symbol_temp_make (); } - else + + /* Total length of the information. */ + exp->X_op = O_subtract; + exp->X_add_symbol = end_sym; + exp->X_op_symbol = start_sym; + + switch (DWARF2_FORMAT (sec)) { - as_fatal (_("internal error: unknown dwarf2 format")); + case dwarf2_format_32bit: + exp->X_add_number = -4; + emit_expr (exp, 4); + return 4; + + case dwarf2_format_64bit: + exp->X_add_number = -12; + out_four (-1); + emit_expr (exp, 8); + return 8; + + case dwarf2_format_64bit_irix: + exp->X_add_number = -8; + emit_expr (exp, 8); + return 8; } + as_fatal (_("internal error: unknown dwarf2 format")); + return 0; +} + +/* Emit the collected .debug_line data. */ + +static void +out_debug_line (segT line_seg) +{ + expressionS exp; + symbolS *prologue_start, *prologue_end; + symbolS *line_end; + 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 (2); + out_two (DWARF2_LINE_VERSION); /* Length of the prologue following this length. */ - expr.X_op = O_subtract; - expr.X_add_symbol = prologue_end; - expr.X_op_symbol = line_start; - expr.X_add_number = - (4 + 2 + 4); - emit_expr (&expr, sizeof_offset); + prologue_start = symbol_temp_make (); + prologue_end = symbol_temp_make (); + exp.X_op = O_subtract; + exp.X_add_symbol = prologue_end; + exp.X_op_symbol = prologue_start; + exp.X_add_number = 0; + emit_expr (&exp, sizeof_offset); + symbol_set_value_now (prologue_start); /* Parameters of the state machine. */ out_byte (DWARF2_LINE_MIN_INSN_LENGTH); @@ -1346,7 +1860,21 @@ out_debug_line (segT line_seg) /* For each section, emit a statement program. */ for (s = all_segs; s; s = s->next) - process_entries (s->seg, s->head->head); + if (SEG_NORMAL (s->seg)) + process_entries (s->seg, s->head->head); + else + as_warn ("dwarf line number information for %s ignored", + segment_name (s->seg)); + + if (flag_dwarf_sections) + /* We have to switch to the special .debug_line_end section + before emitting the end-of-debug_line symbol. The linker + script arranges for this section to be placed after all the + (potentially garbage collected) .debug_line. sections. + This section contains the line_end symbol which is used to + compute the size of the linked .debug_line section, as seen + in the DWARF Line Number header. */ + subseg_set (subseg_get (".debug_line_end", FALSE), 0); symbol_set_value_now (line_end); } @@ -1356,15 +1884,16 @@ out_debug_ranges (segT ranges_seg) { unsigned int addr_size = sizeof_address; struct line_seg *s; - expressionS expr; + expressionS exp; unsigned int i; + memset (&exp, 0, sizeof exp); subseg_set (ranges_seg, 0); /* Base Address Entry. */ - for (i = 0; i < addr_size; i++) + for (i = 0; i < addr_size; i++) out_byte (0xff); - for (i = 0; i < addr_size; i++) + for (i = 0; i < addr_size; i++) out_byte (0); /* Range List Entry. */ @@ -1381,21 +1910,21 @@ out_debug_ranges (segT ranges_seg) end = symbol_temp_new (s->seg, get_frag_fix (frag, s->seg), frag); s->text_end = end; - expr.X_op = O_symbol; - expr.X_add_symbol = beg; - expr.X_add_number = 0; - emit_expr (&expr, addr_size); + exp.X_op = O_symbol; + exp.X_add_symbol = beg; + exp.X_add_number = 0; + emit_expr (&exp, addr_size); - expr.X_op = O_symbol; - expr.X_add_symbol = end; - expr.X_add_number = 0; - emit_expr (&expr, addr_size); + exp.X_op = O_symbol; + exp.X_add_symbol = end; + exp.X_add_number = 0; + emit_expr (&exp, addr_size); } /* End of Range Entry. */ - for (i = 0; i < addr_size; i++) + for (i = 0; i < addr_size; i++) out_byte (0); - for (i = 0; i < addr_size; i++) + for (i = 0; i < addr_size; i++) out_byte (0); } @@ -1405,44 +1934,37 @@ static void out_debug_aranges (segT aranges_seg, segT info_seg) { unsigned int addr_size = sizeof_address; - addressT size, skip; + offsetT size; struct line_seg *s; - expressionS expr; + expressionS exp; + symbolS *aranges_end; char *p; + int sizeof_offset; - size = 4 + 2 + 4 + 1 + 1; - - skip = 2 * addr_size - (size & (2 * addr_size - 1)); - if (skip == 2 * addr_size) - skip = 0; - size += skip; - - for (s = all_segs; s; s = s->next) - size += 2 * addr_size; - - size += 2 * addr_size; - - subseg_set (aranges_seg, 0); - - /* Length of the compilation unit. */ - out_four (size - 4); + memset (&exp, 0, sizeof exp); + sizeof_offset = out_header (aranges_seg, &exp); + aranges_end = exp.X_add_symbol; + size = -exp.X_add_number; /* Version. */ - out_two (2); + out_two (DWARF2_ARANGES_VERSION); + size += 2; /* Offset to .debug_info. */ - /* ??? sizeof_offset */ - TC_DWARF2_EMIT_OFFSET (section_symbol (info_seg), 4); + TC_DWARF2_EMIT_OFFSET (section_symbol (info_seg), sizeof_offset); + size += sizeof_offset; /* Size of an address (offset portion). */ out_byte (addr_size); + size++; /* Size of a segment descriptor. */ out_byte (0); + size++; /* Align the header. */ - if (skip) - frag_align (ffs (2 * addr_size) - 1, 0, 0); + while ((size++ % (2 * addr_size)) > 0) + out_byte (0); for (s = all_segs; s; s = s->next) { @@ -1457,50 +1979,61 @@ out_debug_aranges (segT aranges_seg, segT info_seg) end = symbol_temp_new (s->seg, get_frag_fix (frag, s->seg), frag); s->text_end = end; - expr.X_op = O_symbol; - expr.X_add_symbol = beg; - expr.X_add_number = 0; - emit_expr (&expr, addr_size); + exp.X_op = O_symbol; + exp.X_add_symbol = beg; + exp.X_add_number = 0; + emit_expr (&exp, addr_size); - expr.X_op = O_subtract; - expr.X_add_symbol = end; - expr.X_op_symbol = beg; - expr.X_add_number = 0; - emit_expr (&expr, addr_size); + exp.X_op = O_subtract; + exp.X_add_symbol = end; + exp.X_op_symbol = beg; + exp.X_add_number = 0; + emit_expr (&exp, addr_size); } p = frag_more (2 * addr_size); md_number_to_chars (p, 0, addr_size); md_number_to_chars (p + addr_size, 0, addr_size); + + symbol_set_value_now (aranges_end); } /* Emit data for .debug_abbrev. Note that this must be kept in sync with out_debug_info below. */ static void -out_debug_abbrev (segT abbrev_seg) +out_debug_abbrev (segT abbrev_seg, + segT info_seg ATTRIBUTE_UNUSED, + segT line_seg ATTRIBUTE_UNUSED) { subseg_set (abbrev_seg, 0); out_uleb128 (1); out_uleb128 (DW_TAG_compile_unit); out_byte (DW_CHILDREN_no); - out_abbrev (DW_AT_stmt_list, DW_FORM_data4); + if (DWARF2_FORMAT (line_seg) == dwarf2_format_32bit) + out_abbrev (DW_AT_stmt_list, DW_FORM_data4); + else + out_abbrev (DW_AT_stmt_list, DW_FORM_data8); if (all_segs->next == NULL) { out_abbrev (DW_AT_low_pc, DW_FORM_addr); - out_abbrev (DW_AT_high_pc, DW_FORM_addr); + if (DWARF2_VERSION < 4) + out_abbrev (DW_AT_high_pc, DW_FORM_addr); + else + out_abbrev (DW_AT_high_pc, (sizeof_address == 4 + ? DW_FORM_data4 : DW_FORM_data8)); } else { - if (DWARF2_FORMAT () == dwarf2_format_32bit) + if (DWARF2_FORMAT (info_seg) == dwarf2_format_32bit) out_abbrev (DW_AT_ranges, DW_FORM_data4); else out_abbrev (DW_AT_ranges, DW_FORM_data8); } - out_abbrev (DW_AT_name, DW_FORM_string); - out_abbrev (DW_AT_comp_dir, DW_FORM_string); - out_abbrev (DW_AT_producer, DW_FORM_string); + out_abbrev (DW_AT_name, DW_FORM_strp); + out_abbrev (DW_AT_comp_dir, DW_FORM_strp); + out_abbrev (DW_AT_producer, DW_FORM_strp); out_abbrev (DW_AT_language, DW_FORM_data2); out_abbrev (0, 0); @@ -1511,55 +2044,19 @@ out_debug_abbrev (segT abbrev_seg) /* Emit a description of this compilation unit for .debug_info. */ static void -out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg, segT ranges_seg) +out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg, segT ranges_seg, + symbolS *name_sym, symbolS *comp_dir_sym, symbolS *producer_sym) { - char producer[128]; - char *comp_dir; - expressionS expr; - symbolS *info_start; + expressionS exp; symbolS *info_end; - char *p; - int len; - enum dwarf2_format d2f; int sizeof_offset; - subseg_set (info_seg, 0); - - info_start = symbol_temp_new_now (); - info_end = symbol_temp_make (); - - /* Compilation Unit length. */ - expr.X_op = O_subtract; - expr.X_add_symbol = info_end; - expr.X_op_symbol = info_start; - - d2f = DWARF2_FORMAT (); - if (d2f == dwarf2_format_32bit) - { - expr.X_add_number = -4; - emit_expr (&expr, 4); - sizeof_offset = 4; - } - else if (d2f == dwarf2_format_64bit) - { - expr.X_add_number = -12; - out_four (-1); - emit_expr (&expr, 8); - sizeof_offset = 8; - } - else if (d2f == dwarf2_format_64bit_irix) - { - expr.X_add_number = -8; - emit_expr (&expr, 8); - sizeof_offset = 8; - } - else - { - as_fatal (_("internal error: unknown dwarf2 format")); - } + memset (&exp, 0, sizeof exp); + sizeof_offset = out_header (info_seg, &exp); + info_end = exp.X_add_symbol; /* DWARF version. */ - out_two (2); + out_two (DWARF2_VERSION); /* .debug_abbrev offset */ TC_DWARF2_EMIT_OFFSET (section_symbol (abbrev_seg), sizeof_offset); @@ -1571,23 +2068,30 @@ out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg, segT ranges_seg) out_uleb128 (1); /* DW_AT_stmt_list */ - /* ??? sizeof_offset */ - TC_DWARF2_EMIT_OFFSET (section_symbol (line_seg), 4); + TC_DWARF2_EMIT_OFFSET (section_symbol (line_seg), + (DWARF2_FORMAT (line_seg) == dwarf2_format_32bit + ? 4 : 8)); /* These two attributes are emitted if all of the code is contiguous. */ if (all_segs->next == NULL) { /* DW_AT_low_pc */ - expr.X_op = O_symbol; - expr.X_add_symbol = all_segs->text_start; - expr.X_add_number = 0; - emit_expr (&expr, sizeof_address); + exp.X_op = O_symbol; + exp.X_add_symbol = all_segs->text_start; + exp.X_add_number = 0; + emit_expr (&exp, sizeof_address); /* DW_AT_high_pc */ - expr.X_op = O_symbol; - expr.X_add_symbol = all_segs->text_end; - expr.X_add_number = 0; - emit_expr (&expr, sizeof_address); + if (DWARF2_VERSION < 4) + exp.X_op = O_symbol; + else + { + exp.X_op = O_subtract; + exp.X_op_symbol = all_segs->text_start; + } + exp.X_add_symbol = all_segs->text_end; + exp.X_add_number = 0; + emit_expr (&exp, sizeof_address); } else { @@ -1596,49 +2100,99 @@ out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg, segT ranges_seg) TC_DWARF2_EMIT_OFFSET (section_symbol (ranges_seg), sizeof_offset); } + /* DW_AT_name, DW_AT_comp_dir and DW_AT_producer. Symbols in .debug_str + setup in out_debug_str below. */ + TC_DWARF2_EMIT_OFFSET (name_sym, sizeof_offset); + TC_DWARF2_EMIT_OFFSET (comp_dir_sym, sizeof_offset); + TC_DWARF2_EMIT_OFFSET (producer_sym, sizeof_offset); + + /* DW_AT_language. Yes, this is probably not really MIPS, but the + dwarf2 draft has no standard code for assembler. */ + out_two (DW_LANG_Mips_Assembler); + + symbol_set_value_now (info_end); +} + +/* Emit the three debug strings needed in .debug_str and setup symbols + to them for use in out_debug_info. */ +static void +out_debug_str (segT str_seg, symbolS **name_sym, symbolS **comp_dir_sym, + symbolS **producer_sym) +{ + char producer[128]; + const char *comp_dir; + const char *dirname; + char *p; + int len; + + 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. We're not supposed to get called unless at least one line number entry was emitted, so this should always be defined. */ - if (!files || files_in_use < 1) + *name_sym = symbol_temp_new_now_octets (); + if (files_in_use == 0) abort (); if (files[1].dir) { - len = strlen (dirs[files[1].dir]); + dirname = remap_debug_filename (dirs[files[1].dir]); + len = strlen (dirname); +#ifdef TE_VMS + /* Already has trailing slash. */ + p = frag_more (len); + memcpy (p, dirname, len); +#else p = frag_more (len + 1); - memcpy (p, dirs[files[1].dir], len); + memcpy (p, dirname, len); INSERT_DIR_SEPARATOR (p, len); +#endif } len = strlen (files[1].filename) + 1; p = frag_more (len); memcpy (p, files[1].filename, len); /* DW_AT_comp_dir */ - comp_dir = getpwd (); + *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_octets (); sprintf (producer, "GNU AS %s", VERSION); len = strlen (producer) + 1; p = frag_more (len); memcpy (p, producer, len); +} - /* DW_AT_language. Yes, this is probably not really MIPS, but the - dwarf2 draft has no standard code for assembler. */ - out_two (DW_LANG_Mips_Assembler); - - symbol_set_value_now (info_end); +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; } + /* Finish the dwarf2 debug sections. We emit .debug.line if there were any .file/.loc directives, or --gdwarf2 was given, or if the - file has a non-empty .debug_info section. If we emit .debug_line, - and the .debug_info section is empty, we also emit .debug_info, - .debug_aranges and .debug_abbrev. ALL_SEGS will be non-null if - there were any .file/.loc directives, or --gdwarf2 was given and - there were any located instructions emitted. */ + file has a non-empty .debug_info section and an empty .debug_line + section. If we emit .debug_line, and the .debug_info section is + empty, we also emit .debug_info, .debug_aranges and .debug_abbrev. + ALL_SEGS will be non-null if there were any .file/.loc directives, + or --gdwarf2 was given and there were any located instructions + emitted. */ void dwarf2_finish (void) @@ -1647,13 +2201,24 @@ dwarf2_finish (void) struct line_seg *s; segT info_seg; int emit_other_sections = 0; + int empty_debug_line = 0; info_seg = bfd_get_section_by_name (stdoutput, ".debug_info"); emit_other_sections = info_seg == NULL || !seg_not_empty_p (info_seg); - if (!all_segs && emit_other_sections) - /* There is no line information and no non-empty .debug_info - section. */ + line_seg = bfd_get_section_by_name (stdoutput, ".debug_line"); + empty_debug_line = line_seg == NULL || !seg_not_empty_p (line_seg); + + /* We can't construct a new debug_line section if we already have one. + Give an error. */ + if (all_segs && !empty_debug_line) + as_fatal ("duplicate .debug_line sections"); + + if ((!all_segs && emit_other_sections) + || (!emit_other_sections && !empty_debug_line)) + /* If there is no line information and no non-empty .debug_info + section, or if there is both a non-empty .debug_info and a non-empty + .debug_line, then we do nothing. */ return; /* Calculate the size of an address for the target machine. */ @@ -1661,44 +2226,62 @@ 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) { - struct line_subseg *ss = s->head; - struct line_entry **ptail = ss->ptail; + 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 ((ss = ss->next) != NULL) + while ((lss = lss->next) != NULL) { - *ptail = ss->head; - ptail = ss->ptail; + /* 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; } } out_debug_line (line_seg); /* If this is assembler generated line info, and there is no - debug_info already, we need .debug_info and .debug_abbrev - sections as well. */ + debug_info already, we need .debug_info, .debug_abbrev and + .debug_str sections as well. */ if (emit_other_sections) { segT abbrev_seg; segT aranges_seg; segT ranges_seg; + segT str_seg; + symbolS *name_sym, *comp_dir_sym, *producer_sym; + + gas_assert (all_segs); - assert (all_segs); - info_seg = subseg_new (".debug_info", 0); abbrev_seg = subseg_new (".debug_abbrev", 0); aranges_seg = subseg_new (".debug_aranges", 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); + str_seg = subseg_new (".debug_str", 0); + + 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); @@ -1707,14 +2290,55 @@ 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); } out_debug_aranges (aranges_seg, info_seg); - out_debug_abbrev (abbrev_seg); - out_debug_info (info_seg, abbrev_seg, line_seg, ranges_seg); + out_debug_abbrev (abbrev_seg, info_seg, line_seg); + out_debug_str (str_seg, &name_sym, &comp_dir_sym, &producer_sym); + out_debug_info (info_seg, abbrev_seg, line_seg, ranges_seg, + 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; + } } }