blob: 468b3108d3a40ec03f85c70f5480bc8a09ed62b2 [file] [log] [blame]
From 6e9c8e75162740068ecdd1f1a5d693597f9dbdb6 Mon Sep 17 00:00:00 2001
From: Cary Coutant <ccoutant@google.com>
Date: Tue, 23 Dec 2014 15:01:40 -0800
Subject: [PATCH 03/14] gas: add support for DWARF-5 and experimental two-level
line number tables.
This change is forward-port of commit a7c7bcafd2add7ecf8ea2ad7d3d77cf38d46c195
- https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=a7c7bcafd2add7ecf8ea2ad7d3d77cf38d46c195
from upstream branch users/ccoutant/two-level-line-150331
- https://sourceware.org/git/?p=binutils-gdb.git;a=shortlog;h=refs/heads/users/ccoutant/two-level-line-150331
to binutils-2_27-branch.
[Adrian Ratiu: rebased from v2.27 to v2.36.1]
Change-Id: I84e4565576b9b4fbe88c29312cd75e3687a309d8
---
gas/config/obj-elf.c | 2 +
gas/dwarf2dbg.c | 823 ++++++++++++++++++++++++++++++++++++++-----
gas/dwarf2dbg.h | 19 +-
3 files changed, 756 insertions(+), 88 deletions(-)
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 6c0a6ae9ee2..40b3ce296ea 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -129,6 +129,8 @@ static const pseudo_typeS elf_pseudo_table[] =
{ "file", dwarf2_directive_file, 0 },
{ "loc", dwarf2_directive_loc, 0 },
{ "loc_mark_labels", dwarf2_directive_loc_mark_labels, 0 },
+ { "lloc", dwarf2_directive_loc, 1 },
+ { "subprog", dwarf2_directive_subprog, 0 },
/* We need to trap the section changing calls to handle .previous. */
{"data", obj_elf_data, 0},
diff --git a/gas/dwarf2dbg.c b/gas/dwarf2dbg.c
index 4fbd1e38ec2..700131f0ab8 100644
--- a/gas/dwarf2dbg.c
+++ b/gas/dwarf2dbg.c
@@ -45,6 +45,8 @@
#include "dwarf2dbg.h"
#include <filenames.h>
+#include "hash.h"
+
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
/* We need to decide which character to use as a directory separator.
Just because HAVE_DOS_BASED_FILE_SYSTEM is defined, it does not
@@ -106,6 +108,10 @@
#ifndef DWARF2_RNGLISTS_VERSION
#define DWARF2_RNGLISTS_VERSION 5
#endif
+/* If we see .lloc directives, generate an experimental version 6. */
+#ifndef DWARF2_LINE_EXPERIMENTAL_VERSION
+#define DWARF2_LINE_EXPERIMENTAL_VERSION 0xf006
+#endif
#include "subsegs.h"
@@ -131,6 +137,11 @@
"standard_opcode_lengths" table that is emitted below in
out_debug_line(). */
#define DWARF2_LINE_OPCODE_BASE 13
+#define DWARF5_EXPERIMENTAL_LINE_OPCODE_BASE 16
+
+static int opcode_base;
+static int line_base;
+static unsigned int line_range;
#ifndef DWARF2_LINE_BASE
/* Minimum line offset in a special line info. opcode. This value
@@ -143,12 +154,20 @@
# define DWARF2_LINE_RANGE 14
#endif
+/* For two-level line tables, these values work a bit better. */
+#define DWARF5_EXPERIMENTAL_LINE_BASE -3
+#define DWARF5_EXPERIMENTAL_LINE_RANGE 10
+
#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 1
#endif
+#ifndef DWARF2_LINE_MAX_OPS_PER_INSN
+# define DWARF2_LINE_MAX_OPS_PER_INSN 1
+#endif
+
/* Flag that indicates the initial value of the is_stmt_start flag. */
#define DWARF2_LINE_DEFAULT_IS_STMT 1
@@ -158,11 +177,11 @@
/* Given a special op, return the line skip amount. */
#define SPECIAL_LINE(op) \
- (((op) - DWARF2_LINE_OPCODE_BASE)%DWARF2_LINE_RANGE + DWARF2_LINE_BASE)
+ (((op) - opcode_base) % line_range + line_base)
/* Given a special op, return the address skip amount (in units of
DWARF2_LINE_MIN_INSN_LENGTH. */
-#define SPECIAL_ADDR(op) (((op) - DWARF2_LINE_OPCODE_BASE)/DWARF2_LINE_RANGE)
+#define SPECIAL_ADDR(op) (((op) - opcode_base) / line_range)
/* The maximum address skip amount that can be encoded with a special op. */
#define MAX_SPECIAL_ADDR_DELTA SPECIAL_ADDR(255)
@@ -225,6 +244,44 @@ static char ** dirs = NULL;
static unsigned int dirs_in_use = 0;
static unsigned int dirs_allocated = 0;
+/* Experimental DWARF-5 Extension: Table of subprograms. */
+struct subprog_entry {
+ const char *subpname;
+ unsigned int filenum;
+ unsigned int line;
+};
+
+static struct subprog_entry *subprogs;
+static unsigned int subprogs_in_use;
+static unsigned int subprogs_allocated;
+
+/* Experimental DWARF-5 Extension: Logicals table. */
+struct logicals_entry {
+ segT seg;
+ symbolS *label;
+ /* A logical row doesn't use every field in this struct, but using it
+ here makes the code for writing the line number program simpler. */
+ struct dwarf2_line_info loc;
+ unsigned int context;
+ unsigned int subprog;
+};
+
+static struct logicals_entry *logicals;
+static unsigned int logicals_in_use;
+static unsigned int logicals_allocated = 0;
+static unsigned int logicals_with_labels = 0;
+
+/* DWARF-5: .debug_line_str string table. */
+struct string_table {
+ htab_t hashtab;
+ const char **strings;
+ unsigned int strings_in_use;
+ unsigned int strings_allocated;
+ offsetT next_offset;
+};
+
+static struct string_table debug_line_str_table;
+
/* TRUE when we've seen a .loc directive recently. Used to avoid
doing work when there's nothing to do. Will be reset by
dwarf2_consume_line_info. */
@@ -242,9 +299,9 @@ bfd_boolean dwarf2_loc_mark_labels;
/* Current location as indicated by the most recent .loc directive. */
static struct dwarf2_line_info current =
{
- 1, 1, 0, 0,
- DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0,
- 0, NULL
+ 1, 1, 0, 0, /* filenum, line, column, isa */
+ DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0, /* flags */
+ 0, NULL, 0 /* discriminator, view, logical */
};
/* This symbol is used to recognize view number forced resets in loc
@@ -582,6 +639,15 @@ dwarf2_gen_line_info (addressT ofs, struct dwarf2_line_info *loc)
else
sym = symbol_temp_new (now_seg, frag_now, ofs);
dwarf2_gen_line_info_1 (sym, loc);
+
+ /* Record the current symbol with all logical rows created since
+ the last emitted instruction. */
+ while (logicals_with_labels < logicals_in_use)
+ {
+ logicals[logicals_with_labels].label = sym;
+ logicals[logicals_with_labels].seg = now_seg;
+ logicals_with_labels++;
+ }
}
static const char *
@@ -916,6 +982,7 @@ dwarf2_where (struct dwarf2_line_info *line)
line->isa = current.isa;
line->discriminator = current.discriminator;
line->view = NULL;
+ line->logical = 0;
}
else
*line = current;
@@ -1022,6 +1089,68 @@ dwarf2_emit_label (symbolS *label)
dwarf2_consume_line_info ();
}
+/* Make a new entry in the subprograms table. */
+
+static void
+make_subprog_entry (unsigned int num, char *subpname, int filenum, int line)
+{
+ if (subprogs_allocated == 0)
+ {
+ subprogs_allocated = 4;
+ subprogs = (struct subprog_entry *)
+ xcalloc (subprogs_allocated, sizeof (struct subprog_entry));
+ }
+ if (num > subprogs_allocated)
+ {
+ unsigned int old = subprogs_allocated;
+
+ subprogs_allocated *= 2;
+ if (num > subprogs_allocated)
+ subprogs_allocated = num;
+ subprogs = (struct subprog_entry *)
+ xrealloc (subprogs,
+ subprogs_allocated * sizeof (struct subprog_entry));
+ memset (subprogs + old, 0,
+ (subprogs_allocated - old) * sizeof (struct subprog_entry));
+ }
+ if (subprogs_in_use < num)
+ subprogs_in_use = num;
+ subprogs[num - 1].subpname = xstrdup (subpname);
+ subprogs[num - 1].filenum = filenum;
+ subprogs[num - 1].line = line;
+}
+
+/* Make a new entry in the logicals table. */
+
+static void
+make_logical (unsigned int logical, int context, int subprog)
+{
+ if (logicals_allocated == 0)
+ {
+ logicals_allocated = 4;
+ logicals = (struct logicals_entry *)
+ xcalloc (logicals_allocated, sizeof (struct logicals_entry));
+ }
+ if (logical > logicals_allocated)
+ {
+ unsigned int old = logicals_allocated;
+
+ logicals_allocated *= 2;
+ if (logical > logicals_allocated)
+ logicals_allocated = logical;
+ logicals = (struct logicals_entry *)
+ xrealloc (logicals,
+ logicals_allocated * sizeof (struct logicals_entry));
+ memset (logicals + old, 0,
+ (logicals_allocated - old) * sizeof (struct logicals_entry));
+ }
+ logicals[logical - 1].loc = current;
+ logicals[logical - 1].context = context;
+ logicals[logical - 1].subprog = subprog;
+ if (logical > logicals_in_use)
+ logicals_in_use = logical;
+}
+
/* 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
@@ -1136,40 +1265,130 @@ dwarf2_directive_file (int dummy ATTRIBUTE_UNUSED)
(void) dwarf2_directive_filename ();
}
+/* Experimental DWARF-5 extension:
+ Implements the .subprog SUBPNO ["SUBPROG" [FILENO LINENO]] directive.
+ FILENO is the file number, LINENO the line number and the
+ (optional) COLUMN the column of the source code that the following
+ instruction corresponds to. FILENO can be 0 to indicate that the
+ filename specified by the textually most recent .file directive
+ should be used. */
void
-dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
+dwarf2_directive_subprog (int dummy ATTRIBUTE_UNUSED)
{
- offsetT filenum, line;
+ offsetT num, filenum, line;
+ char *subpname;
+ int subpname_len;
- /* 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);
+ num = get_absolute_expression ();
+ subpname = demand_copy_C_string (&subpname_len);
+ if (subpname == NULL)
+ return;
+ SKIP_WHITESPACE ();
filenum = get_absolute_expression ();
SKIP_WHITESPACE ();
line = get_absolute_expression ();
+ demand_empty_rest_of_line ();
- if (filenum < 1)
+ if (num < 1)
{
- if (filenum == 0 && dwarf_level < 5)
- dwarf_level = 5;
- if (filenum < 0 || DWARF2_LINE_VERSION < 5)
+ as_bad (_("subprogram number less than one"));
+ return;
+ }
+
+ /* A .subprog directive implies compiler generated debug information is
+ being supplied. Turn off gas generated debug info. */
+ debug_type = DEBUG_NONE;
+
+ if (num < (int) subprogs_in_use && subprogs[num].subpname != NULL)
+ {
+ as_bad (_("subprogram number %ld already allocated"), (long) num);
+ return;
+ }
+
+ make_subprog_entry (num, subpname, filenum, line);
+}
+
+void
+dwarf2_directive_loc (int is_lloc)
+{
+ offsetT filenum, line;
+ offsetT logical = 0;
+ offsetT context = 0;
+ offsetT subprog = 0;
+ bfd_boolean is_new_logical = FALSE;
+ bfd_boolean is_actual = FALSE;
+ static bfd_boolean saw_loc = FALSE;
+ static bfd_boolean saw_lloc = FALSE;
+ static bfd_boolean saw_both = FALSE;
+
+ if ((is_lloc && saw_loc) || (!is_lloc && saw_lloc))
+ {
+ if (!saw_both)
+ as_bad (_(".loc and .lloc cannot both be used"));
+ saw_both = TRUE;
+ return;
+ }
+
+ if (is_lloc)
+ {
+ saw_lloc = TRUE;
+ logical = get_absolute_expression ();
+ SKIP_WHITESPACE ();
+
+ if (ISDIGIT (*input_line_pointer))
+ is_new_logical = TRUE;
+ else
+ is_actual = TRUE;
+
+ if (logical < 1)
{
- as_bad (_("file number less than one"));
+ as_bad (_("logical row less than one"));
+ return;
+ }
+ if (is_actual &&
+ ((unsigned int) logical > logicals_in_use
+ || logicals[logical - 1].loc.line == 0))
+ {
+ as_bad (_("unassigned logical row %ld"), (long) logical);
return;
}
}
+ else
+ saw_loc = TRUE;
+
+ /* 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);
- if ((valueT) filenum >= files_in_use || files[filenum].filename == NULL)
+ if (is_lloc && !is_new_logical)
{
- as_bad (_("unassigned file number %ld"), (long) filenum);
- return;
+ filenum = logicals[logical - 1].loc.filenum;
+ line = logicals[logical - 1].loc.line;
+ }
+ else
+ {
+ filenum = get_absolute_expression ();
+ SKIP_WHITESPACE ();
+ line = get_absolute_expression ();
+
+ 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;
+ }
}
current.filenum = filenum;
current.line = line;
current.discriminator = 0;
+ current.logical = logical;
#ifndef NO_LISTING
if (listing)
@@ -1212,17 +1431,17 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
current.flags |= DWARF2_FLAG_BASIC_BLOCK;
*input_line_pointer = c;
}
- else if (strcmp (p, "prologue_end") == 0)
+ else if (!is_actual && strcmp (p, "prologue_end") == 0)
{
current.flags |= DWARF2_FLAG_PROLOGUE_END;
*input_line_pointer = c;
}
- else if (strcmp (p, "epilogue_begin") == 0)
+ else if (!is_actual && strcmp (p, "epilogue_begin") == 0)
{
current.flags |= DWARF2_FLAG_EPILOGUE_BEGIN;
*input_line_pointer = c;
}
- else if (strcmp (p, "is_stmt") == 0)
+ else if (!is_actual && strcmp (p, "is_stmt") == 0)
{
(void) restore_line_pointer (c);
value = get_absolute_expression ();
@@ -1248,7 +1467,7 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
return;
}
}
- else if (strcmp (p, "discriminator") == 0)
+ else if (!is_actual && strcmp (p, "discriminator") == 0)
{
(void) restore_line_pointer (c);
value = get_absolute_expression ();
@@ -1260,7 +1479,7 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
return;
}
}
- else if (strcmp (p, "view") == 0)
+ else if (!is_actual && strcmp (p, "view") == 0)
{
symbolS *sym;
@@ -1311,6 +1530,30 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
}
current.view = sym;
}
+ else if (!is_actual && strcmp (p, "context") == 0)
+ {
+ *input_line_pointer = c;
+ value = get_absolute_expression ();
+ if (value >= 0)
+ context = value;
+ else
+ {
+ as_bad (_("context less than zero"));
+ return;
+ }
+ }
+ else if (!is_actual && strcmp (p, "subprog") == 0)
+ {
+ *input_line_pointer = c;
+ value = get_absolute_expression ();
+ if (value >= 0)
+ subprog = value;
+ else
+ {
+ as_bad (_("subprog number less than zero"));
+ return;
+ }
+ }
else
{
as_bad (_("unknown .loc sub-directive `%s'"), p);
@@ -1328,6 +1571,9 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
/* If we were given a view id, emit the row right away. */
if (current.view)
dwarf2_emit_insn (0);
+
+ if (is_new_logical)
+ make_logical (logical, context, subprog);
}
void
@@ -1459,6 +1705,15 @@ out_set_addr (symbolS *sym)
emit_expr (&exp, sizeof_address);
}
+/* Set the address from a logicals table entry. */
+
+static void
+out_set_addr_from_logical (int logical_delta)
+{
+ out_opcode (DW_LNS_set_address_from_logical);
+ out_leb128 (logical_delta);
+}
+
static void scale_addr_delta (addressT *);
static void
@@ -1497,7 +1752,7 @@ size_inc_line_addr (int line_delta, addressT addr_delta)
to emit the matrix entry. */
if (line_delta == INT_MAX)
{
- if (addr_delta == MAX_SPECIAL_ADDR_DELTA)
+ if (addr_delta == (unsigned int) MAX_SPECIAL_ADDR_DELTA)
len = 1;
else if (addr_delta)
len = 1 + sizeof_leb128 (addr_delta, 0);
@@ -1505,30 +1760,30 @@ size_inc_line_addr (int line_delta, addressT addr_delta)
}
/* Bias the line delta by the base. */
- tmp = line_delta - DWARF2_LINE_BASE;
+ tmp = line_delta - 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 (tmp >= line_range)
{
len = 1 + sizeof_leb128 (line_delta, 1);
line_delta = 0;
- tmp = 0 - DWARF2_LINE_BASE;
+ tmp = 0 - line_base;
}
/* Bias the opcode by the special opcode base. */
- tmp += DWARF2_LINE_OPCODE_BASE;
+ tmp += opcode_base;
/* Avoid overflow when addr_delta is large. */
- if (addr_delta < 256 + MAX_SPECIAL_ADDR_DELTA)
+ if (addr_delta < (unsigned int) (256 + MAX_SPECIAL_ADDR_DELTA))
{
/* Try using a special opcode. */
- opcode = tmp + addr_delta * DWARF2_LINE_RANGE;
+ opcode = tmp + addr_delta * 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;
+ opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * line_range;
if (opcode <= 255)
return len + 2;
}
@@ -1561,7 +1816,7 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len)
to emit the matrix entry. */
if (line_delta == INT_MAX)
{
- if (addr_delta == MAX_SPECIAL_ADDR_DELTA)
+ if (addr_delta == (unsigned int) MAX_SPECIAL_ADDR_DELTA)
*p++ = DW_LNS_const_add_pc;
else if (addr_delta)
{
@@ -1576,17 +1831,17 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len)
}
/* Bias the line delta by the base. */
- tmp = line_delta - DWARF2_LINE_BASE;
+ tmp = line_delta - 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 (tmp >= line_range)
{
*p++ = DW_LNS_advance_line;
p += output_leb128 (p, line_delta, 1);
line_delta = 0;
- tmp = 0 - DWARF2_LINE_BASE;
+ tmp = 0 - line_base;
need_copy = 1;
}
@@ -1599,13 +1854,13 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len)
}
/* Bias the opcode by the special opcode base. */
- tmp += DWARF2_LINE_OPCODE_BASE;
+ tmp += opcode_base;
/* Avoid overflow when addr_delta is large. */
- if (addr_delta < 256 + MAX_SPECIAL_ADDR_DELTA)
+ if (addr_delta < (unsigned int) (256 + MAX_SPECIAL_ADDR_DELTA))
{
/* Try using a special opcode. */
- opcode = tmp + addr_delta * DWARF2_LINE_RANGE;
+ opcode = tmp + addr_delta * line_range;
if (opcode <= 255)
{
*p++ = opcode;
@@ -1613,7 +1868,7 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len)
}
/* Try using DW_LNS_const_add_pc followed by special op. */
- opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * DWARF2_LINE_RANGE;
+ opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * line_range;
if (opcode <= 255)
{
*p++ = DW_LNS_const_add_pc;
@@ -1856,6 +2111,140 @@ dwarf2dbg_convert_frag (fragS *frag)
frag->fr_offset = 0;
}
+/* Generate .debug_line content for the logicals table rows. */
+
+static void
+emit_logicals (void)
+{
+ unsigned logical;
+ unsigned filenum = 1;
+ unsigned line = 1;
+ unsigned column = 0;
+ unsigned discriminator;
+ unsigned flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0;
+ unsigned context = 0;
+ unsigned subprog = 0;
+ segT last_seg = NULL;
+ fragS *last_frag = NULL, *frag;
+ addressT last_frag_ofs = 0, frag_ofs;
+ symbolS *last_lab = NULL, *lab;
+
+ for (logical = 1; logical <= logicals_in_use; ++logical)
+ {
+ int line_delta;
+ int context_delta;
+ struct logicals_entry *e = &logicals[logical - 1];
+
+ discriminator = 0;
+
+ if (context != e->context || subprog != e->subprog)
+ {
+ unsigned int caller = context;
+ unsigned int npop = 0;
+
+ // See if a sequence of DW_LNS_pop_context ops will get
+ // to the state we want.
+ while (caller > 0 && caller <= logicals_in_use)
+ {
+ ++npop;
+ if (logicals[caller - 1].subprog == e->subprog)
+ break;
+ caller = logicals[caller - 1].context;
+ }
+ if (caller > 0 && caller <= logicals_in_use && npop < 10)
+ {
+ while (npop-- > 0)
+ out_opcode (DW_LNS_pop_context);
+ filenum = logicals[caller - 1].loc.filenum;
+ line = logicals[caller - 1].loc.line;
+ column = logicals[caller - 1].loc.column;
+ discriminator = logicals[caller - 1].loc.discriminator;
+ flags = logicals[caller - 1].loc.flags;
+ context = logicals[caller - 1].context;
+ subprog = logicals[caller - 1].subprog;
+ }
+ if (context != e->context && e->context == 0)
+ {
+ context = 0;
+ subprog = e->subprog;
+ out_opcode (DW_LNS_set_subprogram);
+ out_uleb128 (subprog);
+ }
+ else if (context != e->context || subprog != e->subprog)
+ {
+ context_delta = e->context - (logical - 1);
+ context = e->context;
+ subprog = e->subprog;
+ out_opcode (DW_LNS_inlined_call);
+ out_leb128 (context_delta);
+ out_uleb128 (subprog);
+ }
+ }
+
+ if (filenum != e->loc.filenum)
+ {
+ filenum = e->loc.filenum;
+ out_opcode (DW_LNS_set_file);
+ out_uleb128 (filenum);
+ }
+
+ if (column != e->loc.column)
+ {
+ column = e->loc.column;
+ out_opcode (DW_LNS_set_column);
+ out_uleb128 (column);
+ }
+
+ if (e->loc.discriminator != discriminator)
+ {
+ 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 ((e->loc.flags ^ flags) & DWARF2_FLAG_IS_STMT)
+ {
+ flags = e->loc.flags;
+ out_opcode (DW_LNS_negate_stmt);
+ }
+
+ if (e->loc.flags & DWARF2_FLAG_PROLOGUE_END)
+ out_opcode (DW_LNS_set_prologue_end);
+
+ if (e->loc.flags & DWARF2_FLAG_EPILOGUE_BEGIN)
+ out_opcode (DW_LNS_set_epilogue_begin);
+
+ line_delta = e->loc.line - line;
+ if (e->label == NULL)
+ {
+ out_inc_line_addr (line_delta, 0);
+ }
+ else
+ {
+ lab = e->label;
+ frag = symbol_get_frag (lab);
+ frag_ofs = S_GET_VALUE (lab);
+
+ if (last_frag == NULL || e->seg != last_seg)
+ {
+ out_set_addr (lab);
+ out_inc_line_addr (line_delta, 0);
+ }
+ 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);
+
+ line = e->loc.line;
+ last_seg = e->seg;
+ last_lab = lab;
+ last_frag = frag;
+ last_frag_ofs = frag_ofs;
+ }
+ }
+}
+
/* Generate .debug_line content for the chain of line number entries
beginning at E, for segment SEG. */
@@ -1906,27 +2295,30 @@ process_entries (segT seg, struct line_entry *e)
{
int line_delta;
- if (filenum != e->loc.filenum)
- {
- filenum = e->loc.filenum;
- out_opcode (DW_LNS_set_file);
- out_uleb128 (filenum);
- }
+ if (logicals_in_use == 0)
+ {
+ if (filenum != e->loc.filenum)
+ {
+ filenum = e->loc.filenum;
+ out_opcode (DW_LNS_set_file);
+ out_uleb128 (filenum);
+ }
- if (column != e->loc.column)
- {
- column = e->loc.column;
- out_opcode (DW_LNS_set_column);
- out_uleb128 (column);
- }
+ if (column != e->loc.column)
+ {
+ column = e->loc.column;
+ out_opcode (DW_LNS_set_column);
+ 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 (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)
{
@@ -1935,26 +2327,32 @@ process_entries (segT seg, struct line_entry *e)
out_uleb128 (isa);
}
- if ((e->loc.flags ^ flags) & DWARF2_FLAG_IS_STMT)
- {
- flags = e->loc.flags;
- out_opcode (DW_LNS_negate_stmt);
- }
-
if (e->loc.flags & DWARF2_FLAG_BASIC_BLOCK)
out_opcode (DW_LNS_set_basic_block);
- if (e->loc.flags & DWARF2_FLAG_PROLOGUE_END)
- out_opcode (DW_LNS_set_prologue_end);
+ if (logicals_in_use == 0)
+ {
+ if ((e->loc.flags ^ flags) & DWARF2_FLAG_IS_STMT)
+ {
+ flags = e->loc.flags;
+ out_opcode (DW_LNS_negate_stmt);
+ }
- if (e->loc.flags & DWARF2_FLAG_EPILOGUE_BEGIN)
- out_opcode (DW_LNS_set_epilogue_begin);
+ if (e->loc.flags & DWARF2_FLAG_PROLOGUE_END)
+ out_opcode (DW_LNS_set_prologue_end);
+
+ if (e->loc.flags & DWARF2_FLAG_EPILOGUE_BEGIN)
+ out_opcode (DW_LNS_set_epilogue_begin);
+ }
/* 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. */
- line_delta = e->loc.line - line;
+ if (logicals_in_use == 0)
+ line_delta = e->loc.line - line;
+ else
+ line_delta = e->loc.logical - line;
lab = e->label;
frag = symbol_get_frag (lab);
frag_ofs = S_GET_VALUE (lab);
@@ -1973,15 +2371,26 @@ process_entries (segT seg, struct line_entry *e)
&& ((offsetT)last_frag_ofs
>= get_frag_fix (last_frag, seg))))))
{
- out_set_addr (lab);
- out_inc_line_addr (line_delta, 0);
+ if (logicals_in_use > 0 && logicals[e->loc.logical - 1].label == lab)
+ {
+ out_set_addr_from_logical (line_delta);
+ out_opcode (DW_LNS_copy);
+ }
+ else
+ {
+ out_set_addr (lab);
+ out_inc_line_addr (line_delta, 0);
+ }
}
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);
- line = e->loc.line;
+ if (logicals_in_use == 0)
+ line = e->loc.line;
+ else
+ line = e->loc.logical;
last_lab = lab;
last_frag = frag;
last_frag_ofs = frag_ofs;
@@ -2247,6 +2656,160 @@ out_dir_and_file_list (segT line_seg, int sizeof_offset)
out_byte (0);
}
+/* Add a string to the string table. */
+
+static offsetT
+add_to_string_table (struct string_table *strtab, const char *str)
+{
+ const char *key;
+ offsetT val;
+
+ if (strtab->strings_allocated == 0)
+ {
+ strtab->strings_allocated = 4;
+ strtab->strings = (const char **)
+ xcalloc (strtab->strings_allocated, sizeof(char *));
+ strtab->hashtab = str_htab_create ();
+ }
+
+ val = (offsetT) str_hash_find (strtab->hashtab, str);
+ if (val != 0)
+ return val;
+
+ if (strtab->strings_in_use >= strtab->strings_allocated)
+ {
+ unsigned int old = strtab->strings_allocated;
+
+ strtab->strings_allocated *= 2;
+ strtab->strings = (const char **)
+ xrealloc (strtab->strings,
+ strtab->strings_allocated * sizeof (char *));
+ memset (strtab->strings + old, 0,
+ (strtab->strings_allocated - old) * sizeof (char *));
+ }
+
+ key = xstrdup (str);
+ val = strtab->next_offset;
+ str_hash_insert (strtab->hashtab, key, (void *) val, 0);
+ strtab->strings[strtab->strings_in_use++] = key;
+ strtab->next_offset += strlen(key) + 1;
+ return val;
+}
+
+/* Output the string table STRTAB to the section STR_SEG.
+ In a debug string table, the first byte is always '\0',
+ and valid indexes begin at 1. */
+
+static void
+out_string_table (segT str_seg, struct string_table *strtab)
+{
+ unsigned int i;
+ size_t size;
+ char *cp;
+
+ subseg_set (str_seg, 0);
+ out_byte (0);
+ for (i = 0; i < strtab->strings_in_use; i++)
+ {
+ size = strlen (strtab->strings[i]) + 1;
+ cp = frag_more (size);
+ memcpy (cp, strtab->strings[i], size);
+ }
+}
+
+static void
+out_dwarf5_file_list (segT str_seg, int sizeof_offset)
+{
+ const char *dir;
+ offsetT strp;
+ unsigned int i;
+ expressionS exp;
+ unsigned int dir_count = dirs_in_use > 0 ? dirs_in_use - 1 : 0;
+ unsigned int file_count = files_in_use > 0 ? files_in_use - 1 : 0;
+
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = section_symbol (str_seg);
+
+ out_byte (1); /* directory_entry_format_count */
+ out_uleb128 (DW_LNCT_path); /* directory_entry_format[0].content_type */
+ out_uleb128 (DW_FORM_line_strp); /* directory_entry_format[0].form */
+ out_uleb128 (dir_count); /* directories_count */
+
+ /* Emit directories list. */
+ for (i = 1; i < dirs_in_use; ++i)
+ {
+ dir = remap_debug_filename (dirs[i]);
+ strp = add_to_string_table (&debug_line_str_table, dir);
+ exp.X_add_number = strp;
+ emit_expr (&exp, sizeof_offset);
+ }
+
+ out_byte (2); /* file_name_entry_format_count */
+ out_uleb128 (DW_LNCT_path); /* file_name_entry_format[0].type */
+ out_uleb128 (DW_FORM_line_strp); /* file_name_entry_format[0].form */
+ out_uleb128 (DW_LNCT_directory_index); /* file_name_entry_format[0].type */
+ out_uleb128 (DW_FORM_udata); /* file_name_entry_format[0].form */
+ out_uleb128 (file_count); /* file_names_count */
+
+ /* Emit file_names list. */
+ for (i = 1; i < files_in_use; ++i)
+ {
+ const char *fullfilename;
+
+ if (files[i].filename == NULL)
+ {
+ as_bad (_("unassigned file number %ld"), (long) i);
+ /* Prevent a crash later, particularly for file 1. */
+ files[i].filename = "";
+ }
+
+ fullfilename = DWARF2_FILE_NAME (files[i].filename,
+ files[i].dir ? dirs [files [i].dir] : "");
+ strp = add_to_string_table (&debug_line_str_table, fullfilename);
+ exp.X_add_number = strp;
+ emit_expr (&exp, sizeof_offset);
+ out_uleb128 (files[i].dir); /* directory number */
+ }
+}
+
+static void
+out_subprog_list (segT str_seg, int sizeof_offset)
+{
+ const char *name;
+ offsetT strp;
+ unsigned int i;
+ expressionS exp;
+
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = section_symbol (str_seg);
+
+ out_byte (3); /* subprogram_entry_format_count */
+ out_uleb128 (DW_LNCT_subprogram_name); /* subprogram_entry_format[0].type */
+ out_uleb128 (DW_FORM_line_strp); /* subprogram_entry_format[0].form */
+ out_uleb128 (DW_LNCT_decl_file); /* subprogram_entry_format[1].type */
+ out_uleb128 (DW_FORM_udata); /* subprogram_entry_format[1].form */
+ out_uleb128 (DW_LNCT_decl_line); /* subprogram_entry_format[2].type */
+ out_uleb128 (DW_FORM_udata); /* subprogram_entry_format[2].form */
+ out_uleb128 (subprogs_in_use); /* subprograms_count */
+
+ /* Emit subprograms list. */
+ for (i = 0; i < subprogs_in_use; ++i)
+ {
+ name = subprogs[i].subpname;
+ if (name == NULL)
+ {
+ as_bad (_("unassigned subprogram number %ld"), (long) i);
+ strp = 0;
+ }
+ else
+ strp = add_to_string_table (&debug_line_str_table, name);
+ exp.X_add_number = strp;
+ emit_expr (&exp, sizeof_offset);
+ out_uleb128 (subprogs[i].filenum);
+ out_uleb128 (subprogs[i].line);
+ }
+}
+
/* 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
@@ -2306,22 +2869,38 @@ out_header (asection *sec, expressionS *exp)
/* Emit the collected .debug_line data. */
static void
-out_debug_line (segT line_seg)
+out_debug_line (segT line_seg, segT str_seg)
{
expressionS exp;
- symbolS *prologue_start, *prologue_end;
+ symbolS *prologue_start, *prologue_end, *logicals_start, *actuals_start;
symbolS *line_end;
struct line_seg *s;
int sizeof_offset;
+ unsigned int version;
+
+ if (logicals_in_use == 0)
+ {
+ version = DWARF2_LINE_VERSION;
+ opcode_base = DWARF2_LINE_OPCODE_BASE;
+ line_base = DWARF2_LINE_BASE;
+ line_range = DWARF2_LINE_RANGE;
+ }
+ else
+ {
+ version = DWARF2_LINE_EXPERIMENTAL_VERSION;
+ opcode_base = DWARF5_EXPERIMENTAL_LINE_OPCODE_BASE;
+ line_base = DWARF5_EXPERIMENTAL_LINE_BASE;
+ line_range = DWARF5_EXPERIMENTAL_LINE_RANGE;
+ }
memset (&exp, 0, sizeof exp);
sizeof_offset = out_header (line_seg, &exp);
line_end = exp.X_add_symbol;
/* Version. */
- out_two (DWARF2_LINE_VERSION);
+ out_two (version);
- if (DWARF2_LINE_VERSION >= 5)
+ if (version >= 5)
{
out_byte (sizeof_address);
out_byte (0); /* Segment Selector size. */
@@ -2338,12 +2917,12 @@ out_debug_line (segT line_seg)
/* Parameters of the state machine. */
out_byte (DWARF2_LINE_MIN_INSN_LENGTH);
- if (DWARF2_LINE_VERSION >= 4)
+ if (version >= 4)
out_byte (DWARF2_LINE_MAX_OPS_PER_INSN);
out_byte (DWARF2_LINE_DEFAULT_IS_STMT);
- out_byte (DWARF2_LINE_BASE);
- out_byte (DWARF2_LINE_RANGE);
- out_byte (DWARF2_LINE_OPCODE_BASE);
+ out_byte (line_base);
+ out_byte (line_range);
+ out_byte (opcode_base);
/* Standard opcode lengths. */
out_byte (0); /* DW_LNS_copy */
@@ -2360,11 +2939,64 @@ out_debug_line (segT line_seg)
out_byte (1); /* DW_LNS_set_isa */
/* We have emitted 12 opcode lengths, so make that this
matches up to the opcode base value we have been using. */
- gas_assert (DWARF2_LINE_OPCODE_BASE == 13);
+ gas_assert (opcode_base == 13);
+ if (opcode_base == DWARF5_EXPERIMENTAL_LINE_OPCODE_BASE)
+ {
+ out_byte (1); /* DW_LNS_set_subprogram/DW_LNS_set_address_from_logical */
+ out_byte (2); /* DW_LNS_inlined_call */
+ out_byte (0); /* DW_LNS_pop_context */
+ }
+
+ if (version == DWARF2_LINE_EXPERIMENTAL_VERSION)
+ {
+ /* Fake empty version 4 directory and filename lists, to fool
+ old consumers who don't check the version number. */
+ out_byte (0);
+ out_byte (0);
+
+ symbol_set_value_now (prologue_end);
+
+ /* Now wrap the remainder of the section inside a fake
+ extended opcode, so old consumers will see just the single
+ extended opcode, and will not try to read anything else.
+ For simplicity, we simply output a very large number for
+ the size of the extended op. */
+ out_opcode (DW_LNS_extended_op);
+ out_byte (255); /* 3-byte LEB128 for 0x1fffff. */
+ out_byte (255);
+ out_byte (127);
+ out_byte (127); /* Fake extended opcode. */
- out_dir_and_file_list (line_seg, sizeof_offset);
+ /* Logicals table offset. */
+ logicals_start = symbol_temp_make ();
+ exp.X_add_symbol = logicals_start;
+ emit_expr (&exp, sizeof_offset);
- symbol_set_value_now (prologue_end);
+ /* Actuals table offset. */
+ actuals_start = symbol_temp_make ();
+ exp.X_add_symbol = actuals_start;
+ emit_expr (&exp, sizeof_offset);
+
+ /* Directory and filename lists. */
+ out_dwarf5_file_list (str_seg, sizeof_offset);
+
+ /* Subprogram list. */
+ out_subprog_list (str_seg, sizeof_offset);
+
+ symbol_set_value_now (logicals_start);
+ emit_logicals ();
+ symbol_set_value_now (actuals_start);
+ }
+ else if (version >= 5)
+ {
+ out_dwarf5_file_list (str_seg, sizeof_offset);
+ symbol_set_value_now (prologue_end);
+ }
+ else
+ {
+ out_dir_and_file_list (line_seg, sizeof_offset);
+ symbol_set_value_now (prologue_end);
+ }
/* For each section, emit a statement program. */
for (s = all_segs; s; s = s->next)
@@ -2781,11 +3413,13 @@ dwarf2_finish (void)
segT line_seg;
struct line_seg *s;
segT info_seg;
+ segT str_seg = NULL;
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);
+ emit_other_sections = ((info_seg == NULL || !seg_not_empty_p (info_seg))
+ && logicals_in_use == 0);
line_seg = bfd_get_section_by_name (stdoutput, ".debug_line");
empty_debug_line = line_seg == NULL || !seg_not_empty_p (line_seg);
@@ -2839,8 +3473,24 @@ dwarf2_finish (void)
}
}
+ if (logicals_in_use > 0)
+ {
+ str_seg = subseg_new (".debug_line_str", 0);
+ bfd_set_section_flags (str_seg,
+ (SEC_READONLY | SEC_DEBUGGING
+ | SEC_MERGE | SEC_STRINGS));
+ str_seg->entsize = 1;
+ debug_line_str_table.strings = NULL;
+ debug_line_str_table.strings_in_use = 0;
+ debug_line_str_table.strings_allocated = 0;
+ debug_line_str_table.next_offset = 1;
+ }
+
if (empty_debug_line)
- out_debug_line (line_seg);
+ out_debug_line (line_seg, str_seg);
+
+ if (str_seg != NULL)
+ out_string_table (str_seg, &debug_line_str_table);
/* If this is assembler generated line info, and there is no
debug_info already, we need .debug_info, .debug_abbrev and
@@ -2849,7 +3499,6 @@ dwarf2_finish (void)
{
segT abbrev_seg;
segT aranges_seg;
- segT str_seg;
symbolS *name_sym, *comp_dir_sym, *producer_sym, *ranges_sym;
gas_assert (all_segs);
diff --git a/gas/dwarf2dbg.h b/gas/dwarf2dbg.h
index 13824c407d9..9cf786c886a 100644
--- a/gas/dwarf2dbg.h
+++ b/gas/dwarf2dbg.h
@@ -37,6 +37,7 @@ struct dwarf2_line_info
unsigned int flags;
unsigned int discriminator;
symbolS *view;
+ unsigned int logical;
};
/* Implements the .file FILENO "FILENAME" directive. FILENO can be 0
@@ -46,13 +47,29 @@ struct dwarf2_line_info
extern void dwarf2_directive_file (int);
extern char * dwarf2_directive_filename (void);
+/* Experimental DWARF-5 extension:
+ Implements the .subprog SUBPNO ["SUBPROG" [FILENO LINENO]] directive.
+ FILENO is the file number, LINENO the line number and the
+ (optional) COLUMN the column of the source code that the following
+ instruction corresponds to. FILENO can be 0 to indicate that the
+ filename specified by the textually most recent .file directive
+ should be used. */
+extern void dwarf2_directive_subprog (int dummy);
+
/* Implements the .loc FILENO LINENO [COLUMN] directive. FILENO is
the file number, LINENO the line number and the (optional) COLUMN
the column of the source code that the following instruction
corresponds to. FILENO can be 0 to indicate that the filename
specified by the textually most recent .file directive should be
used. */
-extern void dwarf2_directive_loc (int);
+/* Experimental DWARF-5 extension:
+ If IS_LLOC is true, implements the .lloc LOGICAL [FILENO LINENO [COLUMN]]
+ directive. FILENO is the file number, LINENO the line number and the
+ (optional) COLUMN the column of the source code that the following
+ instruction corresponds to. FILENO can be 0 to indicate that the
+ filename specified by the textually most recent .file directive
+ should be used. */
+extern void dwarf2_directive_loc (int is_lloc);
/* Implements the .loc_mark_labels {0,1} directive. */
extern void dwarf2_directive_loc_mark_labels (int);
--
2.32.0