* config/tc-sparc.c (md_pseudo_table): Add .file and .loc.
[binutils-gdb.git] / gas / dwarf2dbg.c
index 230e189d8786c991ebe37953736273bfd4a01784..b09b8dbc2e376b17a52efde7028f484bb6eff23c 100644 (file)
@@ -1,5 +1,5 @@
 /* dwarf2dbg.c - DWARF2 debug support
-   Copyright (C) 1999 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
    Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
 
    This file is part of GAS, the GNU Assembler.
@@ -63,7 +63,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.
@@ -78,7 +78,7 @@
    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: */
+/* Given a special op, return the line skip amount */
 #define SPECIAL_LINE(op) \
        (((op) - DWARF2_LINE_OPCODE_BASE)%DWARF2_LINE_RANGE + DWARF2_LINE_BASE)
 
    DWARF2_LINE_MIN_INSN_LENGTH.  */
 #define SPECIAL_ADDR(op) (((op) - DWARF2_LINE_OPCODE_BASE)/DWARF2_LINE_RANGE)
 
-/* The maximum address skip amont that can be encoded with a special op: */
+/* 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: */                   \
+  /* Initialize as per DWARF2.0 standard.  */                  \
   0,                                   /* address */           \
   1,                                   /* file */              \
   1,                                   /* line */              \
   0,                                   /* basic_block */       \
   1                                    /* empty_sequence */
 
-static struct
-  {
+static struct {
     /* state machine state as per DWARF2 manual: */
-    struct dwarf2_sm
-      {
+    struct dwarf2_sm {
        addressT addr;
        unsigned int filenum;
        unsigned int line;
@@ -112,33 +110,29 @@ static struct
          is_stmt : 1,
          basic_block : 1,
          empty_sequence : 1;           /* current code sequence has no DWARF2 directives? */
-      }
-    sm;
+    } 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
-      {
+    struct {
        int dir;        /* valid after gen_dir_list() only */
        char *name; /* full path before gen_dir_list(), filename afterwards */
-      }
-    *file;
+    } *file;
 
-    struct dwarf2_line_info current;   /* current source info: */
+    struct dwarf2_line_info current;   /* current source info */
 
-    /* counters for statistical purposes: */
+    /* counters for statistical purposes */
     unsigned int num_line_entries;
     unsigned int opcode_hist[256];     /* histogram of opcode frequencies */
-  }
-ls =
-  {
+} ls = {
     {
       INITIAL_STATE
     },
@@ -149,6 +143,7 @@ ls =
     0,
     0,
     0,
+    0,
     NULL,
     { NULL, 0, 0, 0, 0 },
     0,
@@ -170,10 +165,9 @@ ls =
       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: */
+/* Function prototypes.  */
 static void out_uleb128 PARAMS ((addressT));
 static void out_sleb128 PARAMS ((offsetT));
 static void gen_addr_line PARAMS ((int, addressT));
@@ -184,12 +178,13 @@ 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 addressT now_subseg_size PARAMS ((void));
 
 #define out_byte(byte) FRAG_APPEND_1_CHAR(byte)
 #define out_opcode(opc)        (out_byte ((opc)), ++ls.opcode_hist[(opc) & 0xff])
 
 /* Output an unsigned "little-endian base 128" number.  */
+
 static void
 out_uleb128 (value)
      addressT value;
@@ -208,6 +203,7 @@ out_uleb128 (value)
 }
 
 /* Output a signed "little-endian base 128" number.  */
+
 static void
 out_sleb128 (value)
      offsetT value;
@@ -229,6 +225,7 @@ out_sleb128 (value)
 /* 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.  */
+
 static void
 gen_addr_line (line_delta, addr_delta)
      int line_delta;
@@ -248,16 +245,16 @@ gen_addr_line (line_delta, addr_delta)
 
   tmp += DWARF2_LINE_OPCODE_BASE;
 
-  /* try using a special opcode: */
-  opcode = tmp + addr_delta*DWARF2_LINE_RANGE;
+  /* Try using a special opcode.  */
+  opcode = tmp + addr_delta * DWARF2_LINE_RANGE;
   if (opcode <= 255)
     {
       out_opcode (opcode);
       return;
     }
 
-  /* try using DW_LNS_const_add_pc followed by special op: */
-  opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA)*DWARF2_LINE_RANGE;
+  /* 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)
     {
       out_opcode (DW_LNS_const_add_pc);
@@ -269,9 +266,11 @@ gen_addr_line (line_delta, addr_delta)
   out_uleb128 (addr_delta);
 
   if (line_delta)
-    out_opcode (tmp);          /* output line-delta */
+    /* Output line-delta.  */
+    out_opcode (tmp);
   else
-    out_opcode (DW_LNS_copy);  /* append new row with current info */
+    /* Append new row with current info.  */
+    out_opcode (DW_LNS_copy);
 }
 
 static void
@@ -282,7 +281,8 @@ reset_state_machine ()
   ls.sm = initial_state;
 }
 
-/* Set an absolute address (may results in a relocation entry): */
+/* Set an absolute address (may results in a relocation entry).  */
+
 static void
 out_set_addr (addr)
      addressT addr;
@@ -320,10 +320,12 @@ out_set_addr (addr)
 
 /* Emit DW_LNS_end_sequence and reset state machine.  Does not
    preserve the current segment/sub-segment!  */
+
 static void
 out_end_sequence ()
 {
   addressT addr, delta;
+  fragS *text_frag;
 
   if (ls.text_seg)
     {
@@ -333,17 +335,25 @@ out_end_sequence ()
 #else
       addr = frag_now_fix ();
 #endif
+      text_frag = frag_now;
       subseg_set (ls.line_seg, DL_BODY);
-      if (addr < ls.sm.addr)
+      if (text_frag != ls.frag)
        {
          out_set_addr (addr);
          ls.sm.addr = addr;
+         ls.frag = text_frag;
        }
       else
        {
-         delta = addr - ls.sm.addr;
+         delta = (addr - ls.sm.addr) / DWARF2_LINE_MIN_INSN_LENGTH;
          if (delta > 0)
-           gen_addr_line (0, delta / DWARF2_LINE_MIN_INSN_LENGTH);
+           {
+             /* 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
@@ -360,6 +370,7 @@ out_end_sequence ()
    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.  */
+
 static int
 get_filenum (filenum, file)
      int filenum;
@@ -378,7 +389,7 @@ get_filenum (filenum, file)
       && strcmp (ls.file[last].name + 1, file + 1) == 0)
     return last + 1;
 
-  /* no match, fall back to simple linear scan: */
+  /* No match, fall back to simple linear scan.  */
   for (i = 0; i < ls.num_filenames; ++i)
     {
       if (ls.file[i].name[0] == char0
@@ -389,7 +400,7 @@ get_filenum (filenum, file)
        }
     }
 
-  /* no match: enter new filename */
+  /* No match, enter new filename.  */
   if (ls.num_filenames >= ls.filename_len)
     {
       ls.filename_len += 13;
@@ -401,6 +412,9 @@ get_filenum (filenum, file)
   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.  */
+
 void
 dwarf2_gen_line_info (addr, l)
      addressT addr;
@@ -410,6 +424,7 @@ dwarf2_gen_line_info (addr, l)
   unsigned int any_output = 0;
   subsegT saved_subseg;
   segT saved_seg;
+  fragS *saved_frag;
 
   if (flag_debug)
     fprintf (stderr, "line: addr %lx file `%s' line %u col %u flags %x\n",
@@ -426,12 +441,18 @@ dwarf2_gen_line_info (addr, l)
   else if (l->filename)
     filenum = get_filenum (filenum, l->filename);
   else
-    return;    /* no filename, no filnum => no play */
+    /* No filename, no filnum => no play.  */
+    return;
+
+  /* Early out for as-yet incomplete location information.  */
+  if (l->line == 0)
+    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;
 
   if (!ls.line_seg)
     {
@@ -447,9 +468,9 @@ dwarf2_gen_line_info (addr, l)
       /* We're going to need this symbol.  */
       secsym = symbol_find (".debug_line");
       if (secsym != NULL)
-        symbol_set_bfdsym (secsym, ls.line_seg->symbol);
+       symbol_set_bfdsym (secsym, ls.line_seg->symbol);
       else
-        symbol_table_insert (section_symbol (ls.line_seg));
+       symbol_table_insert (section_symbol (ls.line_seg));
 #endif
     }
 
@@ -459,7 +480,8 @@ dwarf2_gen_line_info (addr, l)
     {
       if (!ls.sm.empty_sequence)
        {
-         out_end_sequence ();          /* terminate previous sequence */
+         /* Terminate previous sequence.  */
+         out_end_sequence ();
          ls.sm.empty_sequence = 1;
        }
       any_output = 1;
@@ -467,6 +489,7 @@ dwarf2_gen_line_info (addr, l)
       ls.text_subseg = saved_subseg;
       out_set_addr (addr);
       ls.sm.addr = addr;
+      ls.frag = saved_frag;
     }
 
   if (ls.sm.filenum != filenum)
@@ -500,18 +523,15 @@ dwarf2_gen_line_info (addr, l)
   if (ls.sm.line != l->line)
     {
       any_output = 1;
-      if (addr < ls.sm.addr)
+      if (saved_frag != ls.frag)
        {
-         /* This happens when a new frag got allocated (for whatever
-            reason).  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).
-
-            ??? Perhaps we should directly check for a change of
-            frag_now instead?  */
+         /* 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);
@@ -554,7 +574,7 @@ gen_dir_list ()
            }
          if (j >= num_dirs)
            {
-             /* didn't find this directory: append it to the list */
+             /* Didn't find this directory: append it to the list.  */
              size_t size = strlen (str) + 1;
              cp = frag_more (size);
              memcpy (cp, str, size);
@@ -564,7 +584,9 @@ gen_dir_list ()
          ls.file[i].name = slash + 1;
        }
     }
-  out_byte ('\0');     /* terminate directory list */
+
+  /* Terminate directory list.  */
+  out_byte ('\0');
 }
 
 static void
@@ -584,19 +606,20 @@ gen_file_list ()
       out_uleb128 (0);                 /* last modification timestamp */
       out_uleb128 (0);                 /* filesize */
     }
-  out_byte (0);                /* terminate filename list */
+
+  /* Terminate filename list.  */
+  out_byte (0);
 }
 
 static void
 print_stats (total_size)
      unsigned long total_size;
 {
-  static const char *opc_name[] =
-    {
-      "extended", "copy", "advance_pc", "advance_line", "set_file",
-      "set_column", "negate_stmt", "set_basic_block", "const_add_pc",
-      "fixed_advance_pc"
-    };
+  static const char *opc_name[] = {
+    "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;
 
@@ -605,7 +628,7 @@ print_stats (total_size)
 
   fprintf (stderr, "\nStandard opcode histogram:\n");
 
-  for (i = 0; i < sizeof (opc_name)/sizeof (opc_name[0]); ++i)
+  for (i = 0; i < sizeof (opc_name) / sizeof (opc_name[0]); ++i)
     {
       fprintf (stderr, "%s", opc_name[i]);
       for (j = strlen (opc_name[i]); j < 16; ++j)
@@ -625,12 +648,29 @@ print_stats (total_size)
       j = SPECIAL_LINE (i);
       if (j == DWARF2_LINE_BASE)
        fprintf (stderr, "\n%4u: ",
-                DWARF2_LINE_MIN_INSN_LENGTH*SPECIAL_ADDR (i));
+                (unsigned int) (DWARF2_LINE_MIN_INSN_LENGTH
+                                * SPECIAL_ADDR (i)));
       fprintf (stderr, " %2u", ls.opcode_hist[i]);
     }
   fprintf (stderr, "\n");
 }
 
+/* Compute the size of the current subsegment, taking all fragments
+   into account.  Note that we don't generate variant frags, so the
+   fixed portion is all we need to consider.  */
+
+static addressT
+now_subseg_size ()
+{
+  struct frag *f;
+  addressT size = 0;
+
+  for (f = frchain_now->frch_root; f ; f = f->fr_next)
+    size += f->fr_fix;
+
+  return size + frag_now_fix_octets ();
+}
+
 void
 dwarf2_finish ()
 {
@@ -640,7 +680,7 @@ dwarf2_finish ()
   char *cp;
 
   if (!ls.line_seg)
-    /* no .debug_line segment, no work to do... */
+    /* No .debug_line segment, no work to do.  */
     return;
 
   saved_seg = now_seg;
@@ -648,23 +688,29 @@ dwarf2_finish ()
 
   if (!ls.sm.empty_sequence)
     out_end_sequence ();
-  total_size = body_size = frag_now_fix ();
+  subseg_set (ls.line_seg, DL_BODY);
+  total_size = body_size = now_subseg_size ();
 
-  /* now generate the directory and file lists: */
+  /* 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 ();
+  total_size += now_subseg_size ();
 
-  /* and now the header ("statement program prolog", in DWARF2 lingo...) */
+  /* And now the header ("statement program prolog", in DWARF2 lingo...).  */
   subseg_set (ls.line_seg, DL_PROLOG);
 
   cp = frag_more (15 + DWARF2_LINE_OPCODE_BASE - 1);
 
-  total_size += frag_now_fix ();
+  total_size += now_subseg_size ();
   prolog_size = total_size - body_size - 10;
 
-# define STUFF(val,size)       md_number_to_chars (cp, val, size); cp += size;
+#define STUFF(val,size)                                \
+      do {                                     \
+       md_number_to_chars (cp, val, size);     \
+       cp += size;                             \
+      } while (0)
+
   STUFF (total_size - 4, 4);   /* length */
   STUFF (2, 2);                        /* version */
   STUFF (prolog_size, 4);      /* prologue_length */
@@ -685,6 +731,47 @@ dwarf2_finish ()
   STUFF (0, 1);                        /* DW_LNS_const_add_pc */
   STUFF (1, 1);                        /* DW_LNS_fixed_advance_pc */
 
+#undef STUFF
+
+  /* If this is assembler generated line info, add a .debug_info
+     section as well.  */
+  if (debug_type == DEBUG_DWARF2)
+    {
+      segT info_seg = subseg_new (".debug_info", 0);
+      segT abbrev_seg = subseg_new (".debug_abbrev", 0);
+      char *len;
+
+#ifdef BFD_ASSEMBLER
+      bfd_set_section_flags (stdoutput, info_seg, SEC_READONLY);
+      bfd_set_section_flags (stdoutput, abbrev_seg, SEC_READONLY);
+#endif
+
+      subseg_set (info_seg, 0);
+      len = frag_more (4);
+
+#define STUFF(val, size)                       \
+      do {                                     \
+       cp = frag_more (size);                  \
+       md_number_to_chars (cp, (val), (size)); \
+      } while(0)
+
+      STUFF (2, 2);             /* Dwarf version */
+      STUFF (0, 4);            /* Offset into (final!) .debug_abbrev.  */
+
+      /* Pointer size.  */
+#ifdef BFD_ASSEMBLER
+      STUFF (bfd_arch_bits_per_address (stdoutput) / 8, 1);
+#else
+      STUFF (4, 1);
+#endif
+
+      /* FIXME: Add a DW_TAG_compile_unit DIE.  The line info cannot
+        even be seen without it.  */
+
+      /* Set size of debug_info.  */
+      md_number_to_chars (len, now_subseg_size () - 4, 4);
+    }
+
   subseg_set (saved_seg, saved_subseg);
 
   if (flag_debug)
@@ -707,11 +794,6 @@ dwarf2_directive_file (dummy)
 
   ls.any_dwarf2_directives = 1;
 
-  if (debug_type == DEBUG_NONE)
-    /* Automatically turn on DWARF2 debug info unless something else
-       has been selected.  */
-    debug_type = DEBUG_DWARF2;
-
   ls.current.filenum = get_absolute_expression ();
   ls.current.filename = demand_copy_C_string (&len);
 
@@ -743,13 +825,43 @@ void
 dwarf2_where (line)
      struct dwarf2_line_info *line;
 {
-  if (ls.any_dwarf2_directives)
-    *line = ls.current;
-  else
+  if (debug_type == DEBUG_DWARF2)
     {
       as_where (&line->filename, &line->line);
       line->filenum = 0;
       line->column = 0;
       line->flags = DWARF2_FLAG_BEGIN_STMT;
     }
+  else
+    *line = ls.current;
+}
+
+/* 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.  */
+
+void
+dwarf2_emit_insn (size)
+     int size;
+{
+  addressT addr;
+  struct dwarf2_line_info debug_line;
+
+  if (debug_type != DEBUG_DWARF2 && ! ls.any_dwarf2_directives)
+    return;
+
+  /* Reset any_dwarf2_directives so that we won't waste time 
+     determining that no information has changed between insns.  */
+  ls.any_dwarf2_directives = 0;
+     
+  /* First update the notion of the current source line.  */
+  dwarf2_where (&debug_line);
+
+  /* We want the offset of the start of this
+     instruction within the the current frag.  */
+  addr = frag_now_fix () - size;
+
+  /* And record the information.  */
+  dwarf2_gen_line_info (addr, &debug_line);
 }