| 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 |
| |