X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gas%2Fdwarf2dbg.c;h=e58f73a25cfeb16df2dd70e628a84a92c968911c;hb=0d18235fb6cefd77bcb3645f3d334802452e693f;hp=bd23b5a2a69c9e3ac70cb6d1073313b8fcfcebb8;hpb=353e2c6994b3167b548f1493efe641ac685959e1;p=binutils-gdb.git diff --git a/gas/dwarf2dbg.c b/gas/dwarf2dbg.c index bd23b5a2a69..e58f73a25cf 100644 --- a/gas/dwarf2dbg.c +++ b/gas/dwarf2dbg.c @@ -1,5 +1,5 @@ /* dwarf2dbg.c - DWARF2 debug support - Copyright (C) 1999, 2000 Free Software Foundation, Inc. + Copyright 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. Contributed by David Mosberger-Tang This file is part of GAS, the GNU Assembler. @@ -28,9 +28,36 @@ FILENO is the filenumber. */ #include "ansidecl.h" - #include "as.h" + +#ifdef HAVE_LIMITS_H +#include +#else +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifndef INT_MAX +#define INT_MAX (int) (((unsigned) (-1)) >> 1) +#endif +#endif + #include "dwarf2dbg.h" +#include + +#ifndef DWARF2_FORMAT +# define DWARF2_FORMAT() dwarf2_format_32bit +#endif + +#ifndef DWARF2_ADDR_SIZE +# define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8); +#endif + +#ifndef TC_DWARF2_EMIT_OFFSET +# define TC_DWARF2_EMIT_OFFSET generic_dwarf2_emit_offset +#endif + +#ifdef BFD_ASSEMBLER + #include "subsegs.h" #include "elf/dwarf2.h" @@ -63,7 +90,7 @@ #ifndef DWARF2_LINE_MIN_INSN_LENGTH /* Define the architecture-dependent minimum instruction length (in bytes). This value should be rather too small than too big. */ -# define DWARF2_LINE_MIN_INSN_LENGTH 4 +# define DWARF2_LINE_MIN_INSN_LENGTH 1 #endif /* Flag that indicates the initial value of the is_stmt_start flag. @@ -72,12 +99,6 @@ is not made available by the GCC front-end. */ #define DWARF2_LINE_DEFAULT_IS_STMT 1 -/* Flag that indicates the initial value of the is_stmt_start flag. - In the present implementation, we do not mark any lines as - the beginning of a source statement, because that information - is not made available by the GCC front-end. */ -#define DWARF2_LINE_DEFAULT_IS_STMT 1 - /* Given a special op, return the line skip amount. */ #define SPECIAL_LINE(op) \ (((op) - DWARF2_LINE_OPCODE_BASE)%DWARF2_LINE_RANGE + DWARF2_LINE_BASE) @@ -89,522 +110,996 @@ /* The maximum address skip amount that can be encoded with a special op. */ #define MAX_SPECIAL_ADDR_DELTA SPECIAL_ADDR(255) -#define INITIAL_STATE \ - /* Initialize as per DWARF2.0 standard. */ \ - 0, /* address */ \ - 1, /* file */ \ - 1, /* line */ \ - 0, /* column */ \ - DWARF2_LINE_DEFAULT_IS_STMT, /* is_stmt */ \ - 0, /* basic_block */ \ - 1 /* empty_sequence */ - -static struct - { - /* state machine state as per DWARF2 manual: */ - struct dwarf2_sm - { - addressT addr; - unsigned int filenum; - unsigned int line; - unsigned int column; - unsigned int - is_stmt : 1, - basic_block : 1, - empty_sequence : 1; /* current code sequence has no DWARF2 directives? */ - } - sm; - - unsigned int - any_dwarf2_directives : 1; /* did we emit any DWARF2 line debug directives? */ - - fragS * frag; /* frag that "addr" is relative to */ - segT text_seg; /* text segment "addr" is relative to */ - subsegT text_subseg; - segT line_seg; /* ".debug_line" segment */ - int last_filename; /* index of last filename that was used */ - int num_filenames; /* index of last filename in use */ - int filename_len; /* length of the filename array */ - struct - { - int dir; /* valid after gen_dir_list() only */ - char *name; /* full path before gen_dir_list(), filename afterwards */ - } - *file; - - struct dwarf2_line_info current; /* current source info */ - - /* counters for statistical purposes */ - unsigned int num_line_entries; - unsigned int opcode_hist[256]; /* histogram of opcode frequencies */ - } -ls = - { - { - INITIAL_STATE - }, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - NULL, - { NULL, 0, 0, 0, 0 }, - 0, - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - } - }; - -/* Function prototypes. */ +struct line_entry { + struct line_entry *next; + fragS *frag; + addressT frag_ofs; + struct dwarf2_line_info loc; +}; + +struct line_subseg { + struct line_subseg *next; + subsegT subseg; + struct line_entry *head; + struct line_entry **ptail; +}; + +struct line_seg { + struct line_seg *next; + segT seg; + struct line_subseg *head; + symbolS *text_start; + symbolS *text_end; +}; + +/* Collects data for all line table entries during assembly. */ +static struct line_seg *all_segs; + +struct file_entry { + const char *filename; + unsigned int dir; +}; + +/* Table of files used by .debug_line. */ +static struct file_entry *files; +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; + +/* 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; + +/* Current location as indicated by the most recent .loc directive. */ +static struct dwarf2_line_info current; + +/* Fake label name. */ +static char const fake_label_name[] = ".L0\001"; + +/* The size of an address on the target. */ +static unsigned int sizeof_address; + +static void generic_dwarf2_emit_offset PARAMS((symbolS *, unsigned int)); +static struct line_subseg *get_line_subseg PARAMS ((segT, subsegT)); +static unsigned int get_filenum PARAMS ((const char *, unsigned int)); +static struct frag *first_frag_for_seg PARAMS ((segT)); +static struct frag *last_frag_for_seg PARAMS ((segT)); +static void out_byte PARAMS ((int)); +static void out_opcode PARAMS ((int)); +static void out_two PARAMS ((int)); +static void out_four PARAMS ((int)); +static void out_abbrev PARAMS ((int, int)); static void out_uleb128 PARAMS ((addressT)); -static void out_sleb128 PARAMS ((offsetT)); -static void gen_addr_line PARAMS ((int, addressT)); -static void reset_state_machine PARAMS ((void)); -static void out_set_addr PARAMS ((addressT)); -static void out_end_sequence PARAMS ((void)); -static int get_filenum PARAMS ((int, char *)); -static void gen_dir_list PARAMS ((void)); -static void gen_file_list PARAMS ((void)); -static void print_stats PARAMS ((unsigned long)); +static symbolS *symbol_new_now PARAMS ((void)); +static void set_symbol_value_now PARAMS ((symbolS *)); +static offsetT get_frag_fix PARAMS ((fragS *)); +static void out_set_addr PARAMS ((segT, fragS *, addressT)); +static int size_inc_line_addr PARAMS ((int, addressT)); +static void emit_inc_line_addr PARAMS ((int, addressT, char *, int)); +static void out_inc_line_addr PARAMS ((int, addressT)); +static void relax_inc_line_addr PARAMS ((int, segT, fragS *, addressT, + fragS *, addressT)); +static void process_entries PARAMS ((segT, struct line_entry *)); +static void out_file_list PARAMS ((void)); +static void out_debug_line PARAMS ((segT)); +static void out_debug_aranges PARAMS ((segT, segT)); +static void out_debug_abbrev PARAMS ((segT)); +static void out_debug_info PARAMS ((segT, segT, segT)); + +/* Create an offset to .dwarf2_*. */ -#define out_byte(byte) FRAG_APPEND_1_CHAR(byte) -#define out_opcode(opc) (out_byte ((opc)), ++ls.opcode_hist[(opc) & 0xff]) +static void +generic_dwarf2_emit_offset (symbol, size) + symbolS *symbol; + unsigned int size; +{ + expressionS expr; -/* Output an unsigned "little-endian base 128" number. */ + expr.X_op = O_symbol; + expr.X_add_symbol = symbol; + expr.X_add_number = 0; + emit_expr (&expr, size); +} -static void -out_uleb128 (value) - addressT value; +/* Find or create an entry for SEG+SUBSEG in ALL_SEGS. */ + +static struct line_subseg * +get_line_subseg (seg, subseg) + segT seg; + subsegT subseg; { - unsigned char byte, more = 0x80; + static segT last_seg; + static subsegT last_subseg; + static struct line_subseg *last_line_subseg; - do + struct line_seg *s; + struct line_subseg **pss, *ss; + + if (seg == last_seg && subseg == last_subseg) + return last_line_subseg; + + for (s = all_segs; s; s = s->next) + if (s->seg == seg) + goto found_seg; + + s = (struct line_seg *) xmalloc (sizeof (*s)); + s->next = all_segs; + s->seg = seg; + s->head = NULL; + all_segs = s; + + found_seg: + for (pss = &s->head; (ss = *pss) != NULL ; pss = &ss->next) { - byte = value & 0x7f; - value >>= 7; - if (value == 0) - more = 0; - out_byte (more | byte); + if (ss->subseg == subseg) + goto found_subseg; + if (ss->subseg > subseg) + break; } - while (more); + + ss = (struct line_subseg *) xmalloc (sizeof (*ss)); + ss->next = *pss; + ss->subseg = subseg; + ss->head = NULL; + ss->ptail = &ss->head; + *pss = ss; + + found_subseg: + last_seg = seg; + last_subseg = subseg; + last_line_subseg = ss; + + return ss; } -/* Output a signed "little-endian base 128" number. */ +/* Record an entry for LOC ocurring at OFS within the current fragment. */ -static void -out_sleb128 (value) - offsetT value; +void +dwarf2_gen_line_info (ofs, loc) + addressT ofs; + struct dwarf2_line_info *loc; { - unsigned char byte, more = 0x80; + struct line_subseg *ss; + struct line_entry *e; + static unsigned int line = -1; + static unsigned int filenum = -1; + + /* Early out for as-yet incomplete location information. */ + if (loc->filenum == 0 || loc->line == 0) + return; + + /* Don't emit sequences of line symbols for the same line when the + symbols apply to assembler code. It is necessary to emit + duplicate line symbols when a compiler asks for them, because GDB + uses them to determine the end of the prologue. */ + if (debug_type == DEBUG_DWARF2 + && line == loc->line && filenum == loc->filenum) + return; + + line = loc->line; + filenum = loc->filenum; - do + e = (struct line_entry *) xmalloc (sizeof (*e)); + e->next = NULL; + e->frag = frag_now; + e->frag_ofs = ofs; + e->loc = *loc; + + ss = get_line_subseg (now_seg, now_subseg); + *ss->ptail = e; + ss->ptail = &e->next; +} + +void +dwarf2_where (line) + struct dwarf2_line_info *line; +{ + if (debug_type == DEBUG_DWARF2) { - byte = value & 0x7f; - value >>= 7; - if (((value == 0) && ((byte & 0x40) == 0)) - || ((value == -1) && ((byte & 0x40) != 0))) - more = 0; - out_byte (more | byte); + char *filename; + as_where (&filename, &line->line); + line->filenum = get_filenum (filename, 0); + line->column = 0; + line->flags = DWARF2_FLAG_BEGIN_STMT; } - while (more); + else + *line = current; } -/* 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. */ +/* Called for each machine instruction, or relatively atomic group of + machine instructions (ie built-in macro). The instruction or group + is SIZE bytes in length. If dwarf2 line number generation is called + for, emit a line statement appropriately. */ -static void -gen_addr_line (line_delta, addr_delta) - int line_delta; - addressT addr_delta; +void +dwarf2_emit_insn (size) + int size; { - unsigned int tmp, opcode; + struct dwarf2_line_info loc; - tmp = line_delta - DWARF2_LINE_BASE; + if (loc_directive_seen) + { + /* 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; + } + else if (debug_type != DEBUG_DWARF2) + return; + else + dwarf2_where (& loc); - if (tmp >= DWARF2_LINE_RANGE) + dwarf2_gen_line_info (frag_now_fix () - size, &loc); +} + +/* 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 (filename, num) + 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) { - out_opcode (DW_LNS_advance_line); - out_sleb128 (line_delta); - tmp = 0 - DWARF2_LINE_BASE; - line_delta = 0; + if (! files[last_used].dir + && strcmp (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 + && IS_DIR_SEPARATOR (filename [last_used_dir_len]) + && strcmp (filename + last_used_dir_len + 1, + files[last_used].filename) == 0) + return last_used; } - tmp += DWARF2_LINE_OPCODE_BASE; + 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; - /* Try using a special opcode. */ - opcode = tmp + addr_delta*DWARF2_LINE_RANGE; - if (opcode <= 255) + dir = 0; + if (dir_len) { - out_opcode (opcode); - return; + --dir_len; + for (dir = 1; dir < dirs_in_use; ++dir) + if (memcmp (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 = (char **) + xrealloc (dirs, (dir + 32) * sizeof (const char *)); + } + + dirs[dir] = xmalloc (dir_len + 1); + memcpy (dirs[dir], filename, dir_len); + dirs[dir][dir_len] = '\0'; + dirs_in_use = dir + 1; + } + } + + if (num == 0) + { + for (i = 1; i < files_in_use; ++i) + if (files[i].dir == dir + && files[i].filename + && strcmp (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 = (struct file_entry *) + xrealloc (files, (i + 32) * sizeof (struct file_entry)); + + memset (files + old, 0, (i + 32 - old) * sizeof (struct file_entry)); + } + + files[i].filename = num ? file : xstrdup (file); + files[i].dir = dir; + 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. */ + +char * +dwarf2_directive_file (dummy) + int dummy ATTRIBUTE_UNUSED; +{ + offsetT num; + char *filename; + int filename_len; + + /* Continue to accept a bare string and pass it off. */ + SKIP_WHITESPACE (); + if (*input_line_pointer == '"') + { + s_app_file (0); + return NULL; + } + + num = get_absolute_expression (); + filename = demand_copy_C_string (&filename_len); + demand_empty_rest_of_line (); + + if (num < 1) + { + as_bad (_("file number less than one")); + return NULL; } - /* Try using DW_LNS_const_add_pc followed by special op. */ - opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * DWARF2_LINE_RANGE; - if (opcode <= 255) + if (num < (int) files_in_use && files[num].filename != 0) { - out_opcode (DW_LNS_const_add_pc); - out_opcode (opcode); + as_bad (_("file number %ld already allocated"), (long) num); + return NULL; + } + + get_filenum (filename, num); + + return filename; +} + +void +dwarf2_directive_loc (dummy) + int dummy ATTRIBUTE_UNUSED; +{ + offsetT filenum, line, column; + + filenum = get_absolute_expression (); + SKIP_WHITESPACE (); + line = get_absolute_expression (); + SKIP_WHITESPACE (); + column = get_absolute_expression (); + demand_empty_rest_of_line (); + + if (filenum < 1) + { + as_bad (_("file number less than one")); + return; + } + if (filenum >= (int) files_in_use || files[filenum].filename == 0) + { + as_bad (_("unassigned file number %ld"), (long) filenum); return; } - out_opcode (DW_LNS_advance_pc); - out_uleb128 (addr_delta); + current.filenum = filenum; + current.line = line; + current.column = column; + current.flags = DWARF2_FLAG_BEGIN_STMT; - if (line_delta) - /* Output line-delta. */ - out_opcode (tmp); - else - /* Append new row with current info. */ - out_opcode (DW_LNS_copy); + loc_directive_seen = TRUE; + +#ifndef NO_LISTING + if (listing) + { + if (files[filenum].dir) + { + 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); + + memcpy (cp, dirs[files[filenum].dir], dir_len); + cp[dir_len] = '/'; + memcpy (cp + dir_len + 1, files[filenum].filename, file_len); + cp[dir_len + file_len + 1] = '\0'; + listing_source_file (cp); + } + else + listing_source_file (files[filenum].filename); + listing_source_line (line); + } +#endif +} + +static struct frag * +first_frag_for_seg (seg) + segT seg; +{ + frchainS *f, *first = NULL; + + for (f = frchain_root; f; f = f->frch_next) + if (f->frch_seg == seg + && (! first || first->frch_subseg > f->frch_subseg)) + first = f; + + return first ? first->frch_root : NULL; +} + +static struct frag * +last_frag_for_seg (seg) + segT seg; +{ + frchainS *f, *last = NULL; + + for (f = frchain_root; f; f = f->frch_next) + if (f->frch_seg == seg + && (! last || last->frch_subseg < f->frch_subseg)) + last= f; + + return last ? last->frch_last : NULL; } + +/* Emit a single byte into the current segment. */ + +static inline void +out_byte (byte) + int byte; +{ + FRAG_APPEND_1_CHAR (byte); +} + +/* Emit a statement program opcode into the current segment. */ + +static inline void +out_opcode (opc) + int opc; +{ + out_byte (opc); +} + +/* Emit a two-byte word into the current segment. */ + +static inline void +out_two (data) + int data; +{ + md_number_to_chars (frag_more (2), data, 2); +} + +/* Emit a four byte word into the current segment. */ + +static inline void +out_four (data) + int data; +{ + md_number_to_chars (frag_more (4), data, 4); +} + +/* Emit an unsigned "little-endian base 128" number. */ static void -reset_state_machine () +out_uleb128 (value) + addressT value; +{ + output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0); +} + +/* Emit a tuple for .debug_abbrev. */ + +static inline void +out_abbrev (name, form) + int name, form; { - static const struct dwarf2_sm initial_state = { INITIAL_STATE }; + out_uleb128 (name); + out_uleb128 (form); +} - ls.sm = initial_state; +/* Create a new fake symbol whose value is the current position. */ + +static symbolS * +symbol_new_now () +{ + return symbol_new (fake_label_name, now_seg, frag_now_fix (), frag_now); } -/* Set an absolute address (may results in a relocation entry). */ +/* Set the value of SYM to the current position in the current segment. */ static void -out_set_addr (addr) - addressT addr; +set_symbol_value_now (sym) + symbolS *sym; { - subsegT saved_subseg; - segT saved_seg; - expressionS expr; - symbolS *sym; - int bytes_per_address; + S_SET_SEGMENT (sym, now_seg); + S_SET_VALUE (sym, frag_now_fix ()); + symbol_set_frag (sym, frag_now); +} - saved_seg = now_seg; - saved_subseg = now_subseg; +/* Get the size of a fragment. */ - subseg_set (ls.text_seg, ls.text_subseg); - sym = symbol_new (".L0\001", now_seg, addr, frag_now); +static offsetT +get_frag_fix (frag) + fragS *frag; +{ + frchainS *fr; - subseg_set (saved_seg, saved_subseg); + if (frag->fr_next) + return frag->fr_fix; -#ifdef BFD_ASSEMBLER - bytes_per_address = bfd_arch_bits_per_address (stdoutput) / 8; -#else - /* FIXME. */ - bytes_per_address = 4; -#endif + /* If a fragment is the last in the chain, special measures must be + taken to find its size before relaxation, since it may be pending + on some subsegment chain. */ + for (fr = frchain_root; fr; fr = fr->frch_next) + if (fr->frch_last == frag) + { + long align_mask = -1 << get_recorded_alignment (fr->frch_seg); + return (((char *) obstack_next_free (&fr->frch_obstack) + - frag->fr_literal) + ~align_mask) & align_mask; + } + + abort (); +} + +/* Set an absolute address (may result in a relocation entry). */ + +static void +out_set_addr (seg, frag, ofs) + segT seg; + fragS *frag; + addressT ofs; +{ + expressionS expr; + symbolS *sym; + + sym = symbol_new (fake_label_name, seg, ofs, frag); out_opcode (DW_LNS_extended_op); - out_uleb128 (bytes_per_address + 1); + 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, bytes_per_address); + emit_expr (&expr, sizeof_address); } -/* Emit DW_LNS_end_sequence and reset state machine. Does not - preserve the current segment/sub-segment! */ +#if DWARF2_LINE_MIN_INSN_LENGTH > 1 +static void scale_addr_delta PARAMS ((addressT *)); static void -out_end_sequence () +scale_addr_delta (addr_delta) + addressT *addr_delta; { - addressT addr, delta; - fragS *text_frag; - - if (ls.text_seg) + static int printed_this = 0; + if (*addr_delta % DWARF2_LINE_MIN_INSN_LENGTH != 0) { - subseg_set (ls.text_seg, ls.text_subseg); -#ifdef md_current_text_addr - addr = md_current_text_addr (); + if (!printed_this) + as_bad("unaligned opcodes detected in executable segment"); + printed_this = 1; + } + *addr_delta /= DWARF2_LINE_MIN_INSN_LENGTH; +} #else - addr = frag_now_fix (); +#define scale_addr_delta(A) #endif - text_frag = frag_now; - subseg_set (ls.line_seg, DL_BODY); - if (text_frag != ls.frag) - { - out_set_addr (addr); - ls.sm.addr = addr; - ls.frag = text_frag; - } - else - { - delta = (addr - ls.sm.addr) / DWARF2_LINE_MIN_INSN_LENGTH; - if (delta > 0) - { - /* Advance address without updating the line-debug - matrix---the end_sequence entry is used only to tell - the debugger the end of the sequence. */ - out_opcode (DW_LNS_advance_pc); - out_uleb128 (delta); - } - } - } - else - subseg_set (ls.line_seg, DL_BODY); - - out_opcode (DW_LNS_extended_op); - out_uleb128 (1); - out_byte (DW_LNE_end_sequence); - reset_state_machine (); -} +/* 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. -/* Look up a filenumber either by filename or by filenumber. If both - a filenumber and a filename are specified, lookup by filename takes - precedence. If the filename cannot be found, it is added to the - filetable and the filenumber for the new entry is returned. */ + The following two routines *must* be kept in sync. This is + enforced by making emit_inc_line_addr abort if we do not emit + exactly the expected number of bytes. */ static int -get_filenum (filenum, file) - int filenum; - char *file; +size_inc_line_addr (line_delta, addr_delta) + int line_delta; + addressT addr_delta; { - int i, last = filenum - 1; - char char0 = file[0]; + unsigned int tmp, opcode; + int len = 0; - /* If filenum is out of range of the filename table, then try using the - table entry returned from the previous call. */ - if (last >= ls.num_filenames || last < 0) - last = ls.last_filename; + /* Scale the address delta by the minimum instruction length. */ + scale_addr_delta (&addr_delta); - /* Do a quick check against the specified or previously used filenum. */ - if (ls.num_filenames > 0 && ls.file[last].name[0] == char0 - && strcmp (ls.file[last].name + 1, file + 1) == 0) - return last + 1; + /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence. + We cannot use special opcodes here, since we want the end_sequence + to emit the matrix entry. */ + if (line_delta == INT_MAX) + { + if (addr_delta == MAX_SPECIAL_ADDR_DELTA) + len = 1; + else + len = 1 + sizeof_leb128 (addr_delta, 0); + return len + 3; + } - /* No match, fall back to simple linear scan. */ - for (i = 0; i < ls.num_filenames; ++i) + /* Bias the line delta by the base. */ + tmp = line_delta - DWARF2_LINE_BASE; + + /* If the line increment is out of range of a special opcode, we + must encode it with DW_LNS_advance_line. */ + if (tmp >= DWARF2_LINE_RANGE) { - if (ls.file[i].name[0] == char0 - && strcmp (ls.file[i].name + 1, file + 1) == 0) - { - ls.last_filename = i; - return i + 1; - } + len = 1 + sizeof_leb128 (line_delta, 1); + line_delta = 0; + tmp = 0 - DWARF2_LINE_BASE; } - /* No match, enter new filename. */ - if (ls.num_filenames >= ls.filename_len) + /* Bias the opcode by the special opcode base. */ + tmp += DWARF2_LINE_OPCODE_BASE; + + /* Avoid overflow when addr_delta is large. */ + if (addr_delta < 256 + MAX_SPECIAL_ADDR_DELTA) { - ls.filename_len += 13; - ls.file = xrealloc (ls.file, ls.filename_len * sizeof (ls.file[0])); + /* Try using a special opcode. */ + opcode = tmp + addr_delta * DWARF2_LINE_RANGE; + if (opcode <= 255) + return len + 1; + + /* Try using DW_LNS_const_add_pc followed by special op. */ + opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * DWARF2_LINE_RANGE; + if (opcode <= 255) + return len + 2; } - ls.file[ls.num_filenames].dir = 0; - ls.file[ls.num_filenames].name = file; - ls.last_filename = ls.num_filenames; - return ++ls.num_filenames; -} -/* Emit an entry in the line number table if the address or line has changed. - ADDR is relative to the current frag in the text section. */ + /* Otherwise use DW_LNS_advance_pc. */ + len += 1 + sizeof_leb128 (addr_delta, 0); -void -dwarf2_gen_line_info (addr, l) - addressT addr; - struct dwarf2_line_info *l; + /* DW_LNS_copy or special opcode. */ + len += 1; + + return len; +} + +static void +emit_inc_line_addr (line_delta, addr_delta, p, len) + int line_delta; + addressT addr_delta; + char *p; + int len; { - unsigned int filenum = l->filenum; - unsigned int any_output = 0; - subsegT saved_subseg; - segT saved_seg; - fragS *saved_frag; + unsigned int tmp, opcode; + int need_copy = 0; + char *end = p + len; - if (flag_debug) - fprintf (stderr, "line: addr %lx file `%s' line %u col %u flags %x\n", - (unsigned long) addr, l->filename, l->line, l->column, l->flags); + /* Scale the address delta by the minimum instruction length. */ + scale_addr_delta (&addr_delta); - if (filenum > 0 && !l->filename) + /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence. + We cannot use special opcodes here, since we want the end_sequence + to emit the matrix entry. */ + if (line_delta == INT_MAX) { - if (filenum >= (unsigned int) ls.num_filenames) + if (addr_delta == MAX_SPECIAL_ADDR_DELTA) + *p++ = DW_LNS_const_add_pc; + else { - as_warn ("Encountered bad file number in line number debug info!"); - return; + *p++ = DW_LNS_advance_pc; + p += output_leb128 (p, addr_delta, 0); } + + *p++ = DW_LNS_extended_op; + *p++ = 1; + *p++ = DW_LNE_end_sequence; + goto done; } - else if (l->filename) - filenum = get_filenum (filenum, l->filename); - else - /* No filename, no filnum => no play. */ - return; - /* Must save these before the subseg_new call, as that call will change - them. */ - saved_seg = now_seg; - saved_subseg = now_subseg; - saved_frag = frag_now; + /* Bias the line delta by the base. */ + tmp = line_delta - DWARF2_LINE_BASE; - if (!ls.line_seg) + /* If the line increment is out of range of a special opcode, we + must encode it with DW_LNS_advance_line. */ + if (tmp >= DWARF2_LINE_RANGE) { -#ifdef BFD_ASSEMBLER - symbolS *secsym; -#endif + *p++ = DW_LNS_advance_line; + p += output_leb128 (p, line_delta, 1); - ls.line_seg = subseg_new (".debug_line", 0); - -#ifdef BFD_ASSEMBLER - bfd_set_section_flags (stdoutput, ls.line_seg, SEC_READONLY); + /* Prettier, I think, to use DW_LNS_copy instead of a + "line +0, addr +0" special opcode. */ + if (addr_delta == 0) + { + *p++ = DW_LNS_copy; + goto done; + } - /* We're going to need this symbol. */ - secsym = symbol_find (".debug_line"); - if (secsym != NULL) - symbol_set_bfdsym (secsym, ls.line_seg->symbol); - else - symbol_table_insert (section_symbol (ls.line_seg)); -#endif + line_delta = 0; + tmp = 0 - DWARF2_LINE_BASE; + need_copy = 1; } - subseg_set (ls.line_seg, DL_BODY); + /* Bias the opcode by the special opcode base. */ + tmp += DWARF2_LINE_OPCODE_BASE; - if (ls.text_seg != saved_seg || ls.text_subseg != saved_subseg) + /* Avoid overflow when addr_delta is large. */ + if (addr_delta < 256 + MAX_SPECIAL_ADDR_DELTA) { - if (!ls.sm.empty_sequence) + /* Try using a special opcode. */ + opcode = tmp + addr_delta * DWARF2_LINE_RANGE; + if (opcode <= 255) { - /* Terminate previous sequence. */ - out_end_sequence (); - ls.sm.empty_sequence = 1; + *p++ = opcode; + goto done; } - any_output = 1; - ls.text_seg = saved_seg; - ls.text_subseg = saved_subseg; - out_set_addr (addr); - ls.sm.addr = addr; - ls.frag = saved_frag; - } - if (ls.sm.filenum != filenum) - { - any_output = 1; - out_opcode (DW_LNS_set_file); - out_uleb128 (filenum); - ls.sm.filenum = filenum; - } - - if (ls.sm.column != l->column) - { - any_output = 1; - out_opcode (DW_LNS_set_column); - out_uleb128 (l->column); - ls.sm.column = l->column; + /* Try using DW_LNS_const_add_pc followed by special op. */ + opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * DWARF2_LINE_RANGE; + if (opcode <= 255) + { + *p++ = DW_LNS_const_add_pc; + *p++ = opcode; + goto done; + } } - if (((l->flags & DWARF2_FLAG_BEGIN_STMT) != 0) != ls.sm.is_stmt) - { - any_output = 1; - out_opcode (DW_LNS_negate_stmt); - } + /* Otherwise use DW_LNS_advance_pc. */ + *p++ = DW_LNS_advance_pc; + p += output_leb128 (p, addr_delta, 0); - if (l->flags & DWARF2_FLAG_BEGIN_BLOCK) - { - any_output = 1; - out_opcode (DW_LNS_set_basic_block); - } + if (need_copy) + *p++ = DW_LNS_copy; + else + *p++ = tmp; - if (ls.sm.line != l->line) - { - any_output = 1; - if (saved_frag != ls.frag) - { - /* If a new frag got allocated (for whatever reason), then - deal with it by generating a reference symbol. Note: no - end_sequence needs to be generated because the address did - not really decrease (only the reference point changed). */ - out_set_addr (addr); - ls.sm.addr = addr; - ls.frag = saved_frag; - } - gen_addr_line (l->line - ls.sm.line, - (addr - ls.sm.addr) / DWARF2_LINE_MIN_INSN_LENGTH); - ls.sm.basic_block = 0; - ls.sm.line = l->line; - ls.sm.addr = addr; - } + done: + assert (p == end); +} - subseg_set (saved_seg, saved_subseg); +/* Handy routine to combine calls to the above two routines. */ - ls.num_line_entries += any_output; - if (any_output) - ls.sm.empty_sequence = 0; +static void +out_inc_line_addr (line_delta, addr_delta) + int line_delta; + addressT addr_delta; +{ + int len = size_inc_line_addr (line_delta, addr_delta); + emit_inc_line_addr (line_delta, addr_delta, frag_more (len), len); } +/* Generate a variant frag that we can use to relax address/line + increments between fragments of the target segment. */ + static void -gen_dir_list () +relax_inc_line_addr (line_delta, seg, to_frag, to_ofs, from_frag, from_ofs) + int line_delta; + segT seg; + fragS *to_frag, *from_frag; + addressT to_ofs, from_ofs; { - char *str, *slash, *dir_list, *dp, *cp; - int i, j, num_dirs; + symbolS *to_sym, *from_sym; + expressionS expr; + int max_chars; + + to_sym = symbol_new (fake_label_name, seg, to_ofs, to_frag); + from_sym = symbol_new (fake_label_name, seg, from_ofs, from_frag); - dir_list = frag_more (0); - num_dirs = 0; + expr.X_op = O_subtract; + expr.X_add_symbol = to_sym; + expr.X_op_symbol = from_sym; + expr.X_add_number = 0; - for (i = 0; i < ls.num_filenames; ++i) + /* 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); + + frag_var (rs_dwarf2dbg, max_chars, max_chars, 1, + make_expr_symbol (&expr), line_delta, NULL); +} + +/* The function estimates the size of a rs_dwarf2dbg variant frag + based on the current values of the symbols. It is called before + the relaxation loop. We set fr_subtype to the expected length. */ + +int +dwarf2dbg_estimate_size_before_relax (frag) + fragS *frag; +{ + offsetT addr_delta; + int size; + + addr_delta = resolve_symbol_value (frag->fr_symbol); + size = size_inc_line_addr (frag->fr_offset, addr_delta); + + frag->fr_subtype = size; + + return size; +} + +/* This function relaxes a rs_dwarf2dbg variant frag based on the + current values of the symbols. fr_subtype is the current length + of the frag. This returns the change in frag length. */ + +int +dwarf2dbg_relax_frag (frag) + fragS *frag; +{ + int old_size, new_size; + + old_size = frag->fr_subtype; + new_size = dwarf2dbg_estimate_size_before_relax (frag); + + return new_size - old_size; +} + +/* This function converts a rs_dwarf2dbg variant frag into a normal + fill frag. This is called after all relaxation has been done. + fr_subtype will be the desired length of the frag. */ + +void +dwarf2dbg_convert_frag (frag) + fragS *frag; +{ + offsetT addr_diff; + + 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); + + 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; + frag->fr_var = 0; + frag->fr_offset = 0; +} + +/* Generate .debug_line content for the chain of line number entries + beginning at E, for segment SEG. */ + +static void +process_entries (seg, e) + segT seg; + struct line_entry *e; +{ + unsigned filenum = 1; + unsigned line = 1; + unsigned column = 0; + unsigned flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_BEGIN_STMT : 0; + fragS *frag = NULL; + fragS *last_frag; + addressT frag_ofs = 0; + addressT last_frag_ofs; + struct line_entry *next; + + while (e) { - str = ls.file[i].name; - slash = strrchr (str, '/'); - if (slash) + int changed = 0; + + if (filenum != e->loc.filenum) { - *slash = '\0'; - for (j = 0, dp = dir_list; j < num_dirs; ++j) - { - if (strcmp (str, dp) == 0) - { - ls.file[i].dir = j + 1; - break; - } - dp += strlen (dp); - } - if (j >= num_dirs) + filenum = e->loc.filenum; + out_opcode (DW_LNS_set_file); + out_uleb128 (filenum); + changed = 1; + } + + if (column != e->loc.column) + { + column = e->loc.column; + out_opcode (DW_LNS_set_column); + out_uleb128 (column); + changed = 1; + } + + if ((e->loc.flags ^ flags) & DWARF2_FLAG_BEGIN_STMT) + { + flags = e->loc.flags; + out_opcode (DW_LNS_negate_stmt); + changed = 1; + } + + if (e->loc.flags & DWARF2_FLAG_BEGIN_BLOCK) + { + out_opcode (DW_LNS_set_basic_block); + changed = 1; + } + + /* Don't try to optimize away redundant entries; gdb wants two + entries for a function where the code starts on the same line as + the {, and there's no way to identify that case here. Trust gcc + to optimize appropriately. */ + if (1 /* line != e->loc.line || changed */) + { + int line_delta = e->loc.line - line; + if (frag == NULL) { - /* Didn't find this directory: append it to the list. */ - size_t size = strlen (str) + 1; - cp = frag_more (size); - memcpy (cp, str, size); - ls.file[i].dir = ++num_dirs; + out_set_addr (seg, e->frag, e->frag_ofs); + out_inc_line_addr (line_delta, 0); } - *slash = '/'; - ls.file[i].name = slash + 1; + else if (frag == e->frag) + out_inc_line_addr (line_delta, e->frag_ofs - frag_ofs); + else + relax_inc_line_addr (line_delta, seg, e->frag, e->frag_ofs, + frag, frag_ofs); + + frag = e->frag; + frag_ofs = e->frag_ofs; + line = e->loc.line; + } + else if (frag == NULL) + { + out_set_addr (seg, e->frag, e->frag_ofs); + frag = e->frag; + frag_ofs = e->frag_ofs; } + + next = e->next; + free (e); + e = next; } - /* Terminate directory list. */ - out_byte ('\0'); + /* Emit a DW_LNE_end_sequence for the end of the section. */ + last_frag = last_frag_for_seg (seg); + last_frag_ofs = get_frag_fix (last_frag); + if (frag == last_frag) + out_inc_line_addr (INT_MAX, last_frag_ofs - frag_ofs); + else + relax_inc_line_addr (INT_MAX, seg, last_frag, last_frag_ofs, + frag, frag_ofs); } +/* Emit the directory and file tables for .debug_line. */ + static void -gen_file_list () +out_file_list () { size_t size; char *cp; - int i; + unsigned int i; - for (i = 0; i < ls.num_filenames; ++i) + /* Emit directory list. */ + for (i = 1; i < dirs_in_use; ++i) { - size = strlen (ls.file[i].name) + 1; + size = strlen (dirs[i]) + 1; cp = frag_more (size); - memcpy (cp, ls.file[i].name, size); + memcpy (cp, dirs[i], size); + } + /* Terminate it. */ + out_byte ('\0'); - out_uleb128 (ls.file[i].dir); /* directory number */ + for (i = 1; i < files_in_use; ++i) + { + 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; + } + + size = strlen (files[i].filename) + 1; + cp = frag_more (size); + memcpy (cp, files[i].filename, size); + + out_uleb128 (files[i].dir); /* directory number */ out_uleb128 (0); /* last modification timestamp */ out_uleb128 (0); /* filesize */ } @@ -613,170 +1108,432 @@ gen_file_list () out_byte (0); } +/* Emit the collected .debug_line data. */ + static void -print_stats (total_size) - unsigned long total_size; +out_debug_line (line_seg) + segT line_seg; { - static const char *opc_name[] = + 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); + + line_start = symbol_new_now (); + prologue_end = symbol_make (fake_label_name); + line_end = symbol_make (fake_label_name); + + /* 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; + + 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) { - "extended", "copy", "advance_pc", "advance_line", "set_file", - "set_column", "negate_stmt", "set_basic_block", "const_add_pc", - "fixed_advance_pc" - }; - size_t i; - int j; + expr.X_add_number = -8; + emit_expr (&expr, 8); + sizeof_offset = 8; + } + else + { + as_fatal (_("internal error: unknown dwarf2 format")); + } + + /* Version. */ + out_two (2); + + /* 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); + + /* Parameters of the state machine. */ + out_byte (DWARF2_LINE_MIN_INSN_LENGTH); + out_byte (DWARF2_LINE_DEFAULT_IS_STMT); + out_byte (DWARF2_LINE_BASE); + out_byte (DWARF2_LINE_RANGE); + out_byte (DWARF2_LINE_OPCODE_BASE); + + /* Standard opcode lengths. */ + out_byte (0); /* DW_LNS_copy */ + out_byte (1); /* DW_LNS_advance_pc */ + out_byte (1); /* DW_LNS_advance_line */ + out_byte (1); /* DW_LNS_set_file */ + out_byte (1); /* DW_LNS_set_column */ + out_byte (0); /* DW_LNS_negate_stmt */ + out_byte (0); /* DW_LNS_set_basic_block */ + out_byte (0); /* DW_LNS_const_add_pc */ + out_byte (1); /* DW_LNS_fixed_advance_pc */ + + out_file_list (); + + set_symbol_value_now (prologue_end); + + /* For each section, emit a statement program. */ + for (s = all_segs; s; s = s->next) + process_entries (s->seg, s->head->head); + + set_symbol_value_now (line_end); +} + +/* Emit data for .debug_aranges. */ - fprintf (stderr, "Average size: %g bytes/line\n", - total_size / (double) ls.num_line_entries); +static void +out_debug_aranges (aranges_seg, info_seg) + segT aranges_seg; + segT info_seg; +{ + unsigned int addr_size = sizeof_address; + addressT size, skip; + struct line_seg *s; + expressionS expr; + char *p; + + 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); + + /* Version. */ + out_two (2); + + /* Offset to .debug_info. */ + /* ??? sizeof_offset */ + TC_DWARF2_EMIT_OFFSET (section_symbol (info_seg), 4); + + /* Size of an address (offset portion). */ + out_byte (addr_size); + + /* Size of a segment descriptor. */ + out_byte (0); - fprintf (stderr, "\nStandard opcode histogram:\n"); + /* Align the header. */ + if (skip) + frag_align (ffs (2 * addr_size) - 1, 0, 0); - for (i = 0; i < sizeof (opc_name) / sizeof (opc_name[0]); ++i) + for (s = all_segs; s; s = s->next) { - fprintf (stderr, "%s", opc_name[i]); - for (j = strlen (opc_name[i]); j < 16; ++j) - fprintf (stderr, " "); - fprintf (stderr, ": %u\n", ls.opcode_hist[i]); + fragS *frag; + symbolS *beg, *end; + + frag = first_frag_for_seg (s->seg); + beg = symbol_new (fake_label_name, s->seg, 0, frag); + s->text_start = beg; + + frag = last_frag_for_seg (s->seg); + end = symbol_new (fake_label_name, s->seg, get_frag_fix (frag), 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); + + 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); } - fprintf (stderr, "\nSpecial opcodes:\naddr\t\t\t\tline skip\n"); + p = frag_more (2 * addr_size); + md_number_to_chars (p, 0, addr_size); + md_number_to_chars (p + addr_size, 0, addr_size); +} + +/* Emit data for .debug_abbrev. Note that this must be kept in + sync with out_debug_info below. */ - fprintf (stderr, "skip: "); - for (j = DWARF2_LINE_BASE; j < DWARF2_LINE_BASE + DWARF2_LINE_RANGE; ++j) - fprintf (stderr, "%3d", j); - fprintf (stderr, "\n-----"); +static void +out_debug_abbrev (abbrev_seg) + segT abbrev_seg; +{ + subseg_set (abbrev_seg, 0); - for (; i < 256; ++i) + out_uleb128 (1); + out_uleb128 (DW_TAG_compile_unit); + out_byte (DW_CHILDREN_no); + out_abbrev (DW_AT_stmt_list, DW_FORM_data4); + if (all_segs->next == NULL) { - j = SPECIAL_LINE (i); - if (j == DWARF2_LINE_BASE) - fprintf (stderr, "\n%4u: ", - ((unsigned int) - DWARF2_LINE_MIN_INSN_LENGTH * SPECIAL_ADDR (i))); - fprintf (stderr, " %2u", ls.opcode_hist[i]); + out_abbrev (DW_AT_low_pc, DW_FORM_addr); + out_abbrev (DW_AT_high_pc, DW_FORM_addr); } - fprintf (stderr, "\n"); + 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_language, DW_FORM_data2); + out_abbrev (0, 0); + + /* Terminate the abbreviations for this compilation unit. */ + out_byte (0); } -void -dwarf2_finish () +/* Emit a description of this compilation unit for .debug_info. */ + +static void +out_debug_info (info_seg, abbrev_seg, line_seg) + segT info_seg; + segT abbrev_seg; + segT line_seg; { - addressT body_size, total_size, prolog_size; - subsegT saved_subseg; - segT saved_seg; - char *cp; + char producer[128]; + char *comp_dir; + expressionS expr; + symbolS *info_start; + symbolS *info_end; + char *p; + int len; + enum dwarf2_format d2f; + int sizeof_offset; - if (!ls.line_seg) - /* No .debug_line segment, no work to do. */ - return; + subseg_set (info_seg, 0); - saved_seg = now_seg; - saved_subseg = now_subseg; + info_start = symbol_new_now (); + info_end = symbol_make (fake_label_name); - if (!ls.sm.empty_sequence) - out_end_sequence (); - total_size = body_size = frag_now_fix (); + /* Compilation Unit length. */ + expr.X_op = O_subtract; + expr.X_add_symbol = info_end; + expr.X_op_symbol = info_start; - /* Now generate the directory and file lists. */ - subseg_set (ls.line_seg, DL_FILES); - gen_dir_list (); - gen_file_list (); - total_size += frag_now_fix (); + 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")); + } - /* And now the header ("statement program prolog", in DWARF2 lingo...). */ - subseg_set (ls.line_seg, DL_PROLOG); + /* DWARF version. */ + out_two (2); - cp = frag_more (15 + DWARF2_LINE_OPCODE_BASE - 1); + /* .debug_abbrev offset */ + TC_DWARF2_EMIT_OFFSET (section_symbol (abbrev_seg), sizeof_offset); - total_size += frag_now_fix (); - prolog_size = total_size - body_size - 10; + /* Target address size. */ + out_byte (sizeof_address); -# define STUFF(val,size) md_number_to_chars (cp, val, size); cp += size; - STUFF (total_size - 4, 4); /* length */ - STUFF (2, 2); /* version */ - STUFF (prolog_size, 4); /* prologue_length */ - STUFF (DWARF2_LINE_MIN_INSN_LENGTH, 1); - STUFF (DWARF2_LINE_DEFAULT_IS_STMT, 1); - STUFF (DWARF2_LINE_BASE, 1); - STUFF (DWARF2_LINE_RANGE, 1); - STUFF (DWARF2_LINE_OPCODE_BASE, 1); + /* DW_TAG_compile_unit DIE abbrev */ + out_uleb128 (1); - /* standard_opcode_lengths: */ - STUFF (0, 1); /* DW_LNS_copy */ - STUFF (1, 1); /* DW_LNS_advance_pc */ - STUFF (1, 1); /* DW_LNS_advance_line */ - STUFF (1, 1); /* DW_LNS_set_file */ - STUFF (1, 1); /* DW_LNS_set_column */ - STUFF (0, 1); /* DW_LNS_negate_stmt */ - STUFF (0, 1); /* DW_LNS_set_basic_block */ - STUFF (0, 1); /* DW_LNS_const_add_pc */ - STUFF (1, 1); /* DW_LNS_fixed_advance_pc */ + /* DW_AT_stmt_list */ + /* ??? sizeof_offset */ + TC_DWARF2_EMIT_OFFSET (section_symbol (line_seg), 4); - subseg_set (saved_seg, saved_subseg); + /* These two attributes may only be emitted if all of the code is + contiguous. Multiple sections are not that. */ + 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); + + /* 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 (flag_debug) - print_stats (total_size); + /* 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) + abort (); + if (files[1].dir) + { + len = strlen (dirs[files[1].dir]); + p = frag_more (len + 1); + memcpy (p, dirs[files[1].dir], len); + p[len] = '/'; + } + len = strlen (files[1].filename) + 1; + p = frag_more (len); + memcpy (p, files[1].filename, len); + + /* DW_AT_comp_dir */ + comp_dir = getpwd (); + len = strlen (comp_dir) + 1; + p = frag_more (len); + memcpy (p, comp_dir, len); + + /* DW_AT_producer */ + 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); + + set_symbol_value_now (info_end); } void -dwarf2_directive_file (dummy) - int dummy ATTRIBUTE_UNUSED; +dwarf2_finish () { - int len; + segT line_seg; + struct line_seg *s; + + /* We don't need to do anything unless: + - Some debug information was recorded via .file/.loc + - or, we are generating DWARF2 information ourself (--gdwarf2) + - or, there is a user-provided .debug_info section which could + reference the file table in the .debug_line section we generate + below. */ + if (all_segs == NULL + && debug_type != DEBUG_DWARF2 + && bfd_get_section_by_name (stdoutput, ".debug_info") == NULL) + return; - /* Continue to accept a bare string and pass it off. */ - SKIP_WHITESPACE (); - if (*input_line_pointer == '"') + /* Calculate the size of an address for the target machine. */ + sizeof_address = DWARF2_ADDR_SIZE (stdoutput); + + /* Create and switch to the line number section. */ + line_seg = subseg_new (".debug_line", 0); + bfd_set_section_flags (stdoutput, line_seg, SEC_READONLY); + + /* For each subsection, chain the debug entries together. */ + for (s = all_segs; s; s = s->next) { - s_app_file (0); - return; + struct line_subseg *ss = s->head; + struct line_entry **ptail = ss->ptail; + + while ((ss = ss->next) != NULL) + { + *ptail = ss->head; + ptail = ss->ptail; + } } - ls.any_dwarf2_directives = 1; + out_debug_line (line_seg); - if (debug_type == DEBUG_NONE) - /* Automatically turn on DWARF2 debug info unless something else - has been selected. */ - debug_type = DEBUG_DWARF2; + /* If this is assembler generated line info, we need .debug_info + and .debug_abbrev sections as well. */ + if (all_segs != NULL && debug_type == DEBUG_DWARF2) + { + segT abbrev_seg; + segT info_seg; + segT aranges_seg; - ls.current.filenum = get_absolute_expression (); - ls.current.filename = demand_copy_C_string (&len); + info_seg = subseg_new (".debug_info", 0); + abbrev_seg = subseg_new (".debug_abbrev", 0); + aranges_seg = subseg_new (".debug_aranges", 0); - demand_empty_rest_of_line (); + bfd_set_section_flags (stdoutput, info_seg, SEC_READONLY); + bfd_set_section_flags (stdoutput, abbrev_seg, SEC_READONLY); + bfd_set_section_flags (stdoutput, aranges_seg, SEC_READONLY); + + record_alignment (aranges_seg, ffs (2 * sizeof_address) - 1); + + out_debug_aranges (aranges_seg, info_seg); + out_debug_abbrev (abbrev_seg); + out_debug_info (info_seg, abbrev_seg, line_seg); + } } +#else void -dwarf2_directive_loc (dummy) - int dummy ATTRIBUTE_UNUSED; +dwarf2_finish () { - ls.any_dwarf2_directives = 1; +} - ls.current.filenum = get_absolute_expression (); - SKIP_WHITESPACE (); - ls.current.line = get_absolute_expression (); - SKIP_WHITESPACE (); - ls.current.column = get_absolute_expression (); - demand_empty_rest_of_line (); +int +dwarf2dbg_estimate_size_before_relax (frag) + fragS *frag ATTRIBUTE_UNUSED; +{ + as_fatal (_("dwarf2 is not supported for this object file format")); + return 0; +} - ls.current.flags = DWARF2_FLAG_BEGIN_STMT; +int +dwarf2dbg_relax_frag (frag) + fragS *frag ATTRIBUTE_UNUSED; +{ + as_fatal (_("dwarf2 is not supported for this object file format")); + return 0; +} -#ifndef NO_LISTING - if (listing) - listing_source_line (ls.current.line); -#endif +void +dwarf2dbg_convert_frag (frag) + fragS *frag ATTRIBUTE_UNUSED; +{ + as_fatal (_("dwarf2 is not supported for this object file format")); } void -dwarf2_where (line) - struct dwarf2_line_info *line; +dwarf2_emit_insn (size) + int size ATTRIBUTE_UNUSED; { - if (ls.any_dwarf2_directives) - *line = ls.current; - else - { - as_where (&line->filename, &line->line); - line->filenum = 0; - line->column = 0; - line->flags = DWARF2_FLAG_BEGIN_STMT; - } } + +char * +dwarf2_directive_file (dummy) + int dummy ATTRIBUTE_UNUSED; +{ + s_app_file (0); + return NULL; +} + +void +dwarf2_directive_loc (dummy) + int dummy ATTRIBUTE_UNUSED; +{ + as_fatal (_("dwarf2 is not supported for this object file format")); +} +#endif /* BFD_ASSEMBLER */