/* dwarf2dbg.c - DWARF2 debug support
- Copyright (C) 1999-2020 Free Software Foundation, Inc.
+ Copyright (C) 1999-2021 Free Software Foundation, Inc.
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of GAS, the GNU Assembler.
#include "as.h"
#include "safe-ctype.h"
-
-#ifdef HAVE_LIMITS_H
#include <limits.h>
-#else
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#ifndef INT_MAX
-#define INT_MAX (int) (((unsigned) (-1)) >> 1)
-#endif
-#endif
-
#include "dwarf2dbg.h"
#include <filenames.h>
{
const char * filename;
unsigned int dir;
- bfd_boolean auto_assigned;
unsigned char md5[NUM_MD5_BYTES];
};
static struct file_entry *files;
static unsigned int files_in_use;
static unsigned int files_allocated;
+static unsigned int num_of_auto_assigned;
/* Table of directories used by .debug_line. */
static char ** dirs = NULL;
static unsigned int dirs_allocated = 0;
/* TRUE when we've seen a .loc directive recently. Used to avoid
- doing work when there's nothing to do. */
-bfd_boolean dwarf2_loc_directive_seen;
+ doing work when there's nothing to do. Will be reset by
+ dwarf2_consume_line_info. */
+bool dwarf2_loc_directive_seen;
+
+/* TRUE when we've seen any .loc directive at any time during parsing.
+ Indicates the user wants us to generate a .debug_line section.
+ Used in dwarf2_finish as sanity check. */
+static bool dwarf2_any_loc_directive_seen;
/* TRUE when we're supposed to set the basic block mark whenever a
label is seen. */
-bfd_boolean dwarf2_loc_mark_labels;
+bool dwarf2_loc_mark_labels;
/* Current location as indicated by the most recent .loc directive. */
static struct dwarf2_line_info current =
/* Find or create (if CREATE_P) an entry for SEG+SUBSEG in ALL_SEGS. */
static struct line_subseg *
-get_line_subseg (segT seg, subsegT subseg, bfd_boolean create_p)
+get_line_subseg (segT seg, subsegT subseg, bool create_p)
{
struct line_seg *s = seg_info (seg)->dwarf2_line_seg;
struct line_subseg **pss, *lss;
{
struct line_subseg *lss;
struct line_entry *e;
+ flagword need_flags = SEC_LOAD | SEC_CODE;
+
+ /* PR 26850: Do not record LOCs in non-executable or non-loaded
+ sections. SEC_ALLOC isn't tested for non-ELF because obj-coff.c
+ obj_coff_section is careless in setting SEC_ALLOC. */
+ if (IS_ELF)
+ need_flags |= SEC_ALLOC;
+ if ((now_seg->flags & need_flags) != need_flags)
+ {
+ /* FIXME: Add code to suppress multiple warnings ? */
+ if (debug_type != DEBUG_DWARF2)
+ as_warn ("dwarf line number information for %s ignored",
+ segment_name (now_seg));
+ return;
+ }
e = XNEW (struct line_entry);
e->next = NULL;
e->label = label;
e->loc = *loc;
- lss = get_line_subseg (now_seg, now_subseg, TRUE);
+ lss = get_line_subseg (now_seg, now_subseg, true);
/* Subseg heads are chained to previous subsegs in
dwarf2_finish. */
/* Early out for as-yet incomplete location information. */
if (loc->line == 0)
return;
- if (loc->filenum == 0 && DWARF2_LINE_VERSION < 5)
- return;
+ if (loc->filenum == 0)
+ {
+ if (dwarf_level < 5)
+ dwarf_level = 5;
+ if (DWARF2_LINE_VERSION < 5)
+ return;
+ }
/* Don't emit sequences of line symbols for the same line when the
symbols apply to assembler code. It is necessary to emit
}
static unsigned int
-get_directory_table_entry (const char * dirname,
- size_t dirlen,
- bfd_boolean can_use_zero)
+get_directory_table_entry (const char *dirname,
+ size_t dirlen,
+ bool can_use_zero)
{
unsigned int d;
return d;
}
-static bfd_boolean
-assign_file_to_slot (unsigned long i, const char *file, unsigned int dir, bfd_boolean auto_assign)
+static bool
+assign_file_to_slot (unsigned long i, const char *file, unsigned int dir)
{
if (i >= files_allocated)
{
if (files_allocated <= old)
{
as_bad (_("file number %lu is too big"), (unsigned long) i);
- return FALSE;
+ return false;
}
files = XRESIZEVEC (struct file_entry, files, files_allocated);
files[i].filename = file;
files[i].dir = dir;
- files[i].auto_assigned = auto_assign;
memset (files[i].md5, 0, NUM_MD5_BYTES);
if (files_in_use < i + 1)
files_in_use = i + 1;
- return TRUE;
+ return true;
}
/* Get a .debug_line file number for PATHNAME. If there is a
file = get_basename (pathname);
dir_len = file - pathname;
- dir = get_directory_table_entry (pathname, dir_len, FALSE);
+ dir = get_directory_table_entry (pathname, dir_len, false);
/* Do not use slot-0. That is specifically reserved for use by
the '.file 0 "name"' directive. */
return i;
}
- if (!assign_file_to_slot (i, file, dir, TRUE))
+ if (!assign_file_to_slot (i, file, dir))
return -1;
+ num_of_auto_assigned++;
+
last_used = i;
last_used_dir_len = dir_len;
if WITH_MD5 is TRUE then there is a md5 value in generic_bignum.
Returns TRUE if allocation succeeded, FALSE otherwise. */
-static bfd_boolean
-allocate_filename_to_slot (const char * dirname,
- const char * filename,
- unsigned int num,
- bfd_boolean with_md5)
+static bool
+allocate_filename_to_slot (const char *dirname,
+ const char *filename,
+ unsigned int num,
+ bool with_md5)
{
const char *file;
size_t dirlen;
{
const char * dir = NULL;
- if (dirs)
+ if (dirs != NULL)
dir = dirs[files[num].dir];
if (with_md5
/* If the filenames match, but the directory table entry was
empty, then fill it with the provided directory name. */
if (dir == NULL)
- dirs[files[num].dir] = xmemdup0 (dirname, strlen (dirname));
+ {
+ if (dirs == NULL)
+ {
+ dirs_allocated = files[num].dir + 32;
+ dirs = XCNEWVEC (char *, dirs_allocated);
+ }
+
+ dirs[files[num].dir] = xmemdup0 (dirname, strlen (dirname));
+ }
- return TRUE;
+ return true;
}
else if (dir != NULL)
{
if (filename_ncmp (filename, dir, dirlen) == 0
&& IS_DIR_SEPARATOR (filename [dirlen])
&& filename_cmp (filename + dirlen + 1, files[num].filename) == 0)
- return TRUE;
+ return true;
}
else /* dir == NULL */
{
file = get_basename (filename);
if (filename_cmp (file, files[num].filename) == 0)
{
+ /* The filenames match, but the directory table entry is empty.
+ Fill it with the provided directory name. */
if (file > filename)
- /* The filenames match, but the directory table entry is empty.
- Fill it with the provided directory name. */
- dirs[files[num].dir] = xmemdup0 (filename, file - filename);
- return TRUE;
+ {
+ if (dirs == NULL)
+ {
+ dirs_allocated = files[num].dir + 32;
+ dirs = XCNEWVEC (char *, dirs_allocated);
+ }
+
+ dirs[files[num].dir] = xmemdup0 (filename, file - filename);
+ }
+ return true;
}
}
fail:
- /* If NUM was previously allocated automatically then
- choose another slot for it, so that we can reuse NUM. */
- if (files[num].auto_assigned)
- {
- /* Find an unused slot. */
- for (i = 1; i < files_in_use; ++i)
- if (files[i].filename == NULL)
- break;
- if (! assign_file_to_slot (i, files[num].filename, files[num].dir, TRUE))
- return FALSE;
- files[num].filename = NULL;
- }
- else
- {
- as_bad (_("file table slot %u is already occupied by a different file (%s%s%s vs %s%s%s)"),
- num,
- dir == NULL ? "" : dir,
- dir == NULL ? "" : "/",
- files[num].filename,
- dirname == NULL ? "" : dirname,
- dirname == NULL ? "" : "/",
- filename);
- return FALSE;
- }
+ as_bad (_("file table slot %u is already occupied by a different file (%s%s%s vs %s%s%s)"),
+ num,
+ dir == NULL ? "" : dir,
+ dir == NULL ? "" : "/",
+ files[num].filename,
+ dirname == NULL ? "" : dirname,
+ dirname == NULL ? "" : "/",
+ filename);
+ return false;
}
if (dirname == NULL)
d = get_directory_table_entry (dirname, dirlen, num == 0);
i = num;
- if (! assign_file_to_slot (i, file, d, FALSE))
- return FALSE;
+ if (! assign_file_to_slot (i, file, d))
+ return false;
if (with_md5)
{
else
memset (files[i].md5, 0, NUM_MD5_BYTES);
- return TRUE;
+ return true;
}
/* Returns the current source information. If .file directives have
if (delta == 0)
return;
- lss = get_line_subseg (now_seg, now_subseg, FALSE);
+ lss = get_line_subseg (now_seg, now_subseg, false);
if (!lss)
return;
{
/* Unless we generate DWARF2 debugging information for each
assembler line, we only emit one line symbol for one LOC. */
- dwarf2_loc_directive_seen = FALSE;
+ dwarf2_loc_directive_seen = false;
current.flags &= ~(DWARF2_FLAG_BASIC_BLOCK
| DWARF2_FLAG_PROLOGUE_END
char *
dwarf2_directive_filename (void)
{
- bfd_boolean with_md5 = FALSE;
+ bool with_md5 = false;
valueT num;
char *filename;
const char * dirname = NULL;
int filename_len;
+ unsigned int i;
/* Continue to accept a bare string and pass it off. */
SKIP_WHITESPACE ();
num = get_absolute_expression ();
- if ((offsetT) num < 1 && DWARF2_LINE_VERSION < 5)
+ if ((offsetT) num < 1)
{
- as_bad (_("file number less than one"));
- ignore_rest_of_line ();
- return NULL;
+ if (num == 0 && dwarf_level < 5)
+ dwarf_level = 5;
+ if ((offsetT) num < 0 || DWARF2_LINE_VERSION < 5)
+ {
+ as_bad (_("file number less than one"));
+ ignore_rest_of_line ();
+ return NULL;
+ }
}
/* FIXME: Should we allow ".file <N>\n" as an expression meaning
SKIP_WHITESPACE ();
}
- if (strncmp (input_line_pointer, "md5", 3) == 0)
+ if (startswith (input_line_pointer, "md5"))
{
input_line_pointer += 3;
SKIP_WHITESPACE ();
if (exp.X_op != O_big)
as_bad (_("md5 value too small or not a constant"));
else
- with_md5 = TRUE;
+ with_md5 = true;
}
}
return NULL;
}
+ if (num_of_auto_assigned)
+ {
+ /* Clear slots auto-assigned before the first .file <NUMBER>
+ directive was seen. */
+ if (files_in_use != (num_of_auto_assigned + 1))
+ abort ();
+ for (i = 1; i < files_in_use; i++)
+ files[i].filename = NULL;
+ files_in_use = 0;
+ num_of_auto_assigned = 0;
+ }
+
if (! allocate_filename_to_slot (dirname, filename, (unsigned int) num,
with_md5))
return NULL;
if (filenum < 1)
{
- if (filenum != 0 || DWARF2_LINE_VERSION < 5)
+ if (filenum == 0 && dwarf_level < 5)
+ dwarf_level = 5;
+ if (filenum < 0 || DWARF2_LINE_VERSION < 5)
{
as_bad (_("file number less than one"));
return;
}
}
- if (filenum >= (int) files_in_use || files[filenum].filename == NULL)
+ if ((valueT) filenum >= files_in_use || files[filenum].filename == NULL)
{
as_bad (_("unassigned file number %ld"), (long) filenum);
return;
if (ISDIGIT (*input_line_pointer)
|| *input_line_pointer == '-')
{
- bfd_boolean force_reset = *input_line_pointer == '-';
+ bool force_reset = *input_line_pointer == '-';
value = get_absolute_expression ();
if (value != 0)
}
demand_empty_rest_of_line ();
- dwarf2_loc_directive_seen = TRUE;
+ dwarf2_any_loc_directive_seen = dwarf2_loc_directive_seen = true;
debug_type = DEBUG_NONE;
/* If we were given a view id, emit the row right away. */
if (strcmp (sec_name, ".text") != 0)
{
name = concat (".debug_line", sec_name, (char *) NULL);
- subseg_set (subseg_get (name, FALSE), 0);
+ subseg_set (subseg_get (name, false), 0);
}
else
/* Don't create a .debug_line.text section -
that is redundant. Instead just switch back to the
normal .debug_line section. */
- subseg_set (subseg_get (".debug_line", FALSE), 0);
+ subseg_set (subseg_get (".debug_line", false), 0);
}
do
}
}
+/* Switch to LINE_STR_SEG and output the given STR. Return the
+ symbol pointing to the new string in the section. */
+
+static symbolS *
+add_line_strp (segT line_str_seg, const char *str)
+{
+ char *cp;
+ size_t size;
+ symbolS *sym;
+
+ subseg_set (line_str_seg, 0);
+
+ sym = symbol_temp_new_now_octets ();
+
+ size = strlen (str) + 1;
+ cp = frag_more (size);
+ memcpy (cp, str, size);
+
+ return sym;
+}
+
+
/* Emit the directory and file tables for .debug_line. */
static void
-out_dir_and_file_list (void)
+out_dir_and_file_list (segT line_seg, int sizeof_offset)
{
size_t size;
const char *dir;
char *cp;
unsigned int i;
- bfd_boolean emit_md5 = FALSE;
- bfd_boolean emit_timestamps = TRUE;
- bfd_boolean emit_filesize = TRUE;
+ bool emit_md5 = false;
+ bool emit_timestamps = true;
+ bool emit_filesize = true;
+ segT line_str_seg = NULL;
+ symbolS *line_strp;
/* Output the Directory Table. */
if (DWARF2_LINE_VERSION >= 5)
/* Describe the purpose and format of the column. */
out_uleb128 (DW_LNCT_path);
- /* FIXME: it would be better to store these strings in
- the .debug_line_str section and reference them here. */
- out_uleb128 (DW_FORM_string);
+ /* Store these strings in the .debug_line_str section so they
+ can be shared. */
+ out_uleb128 (DW_FORM_line_strp);
/* Now state how many rows there are in the table. We need at
least 1 if there is one or more file names to store the
/* Emit directory list. */
if (DWARF2_LINE_VERSION >= 5 && (dirs_in_use > 0 || files_in_use > 0))
{
+ line_str_seg = subseg_new (".debug_line_str", 0);
+ bfd_set_section_flags (line_str_seg,
+ SEC_READONLY | SEC_DEBUGGING | SEC_OCTETS
+ | SEC_MERGE | SEC_STRINGS);
+ line_str_seg->entsize = 1;
+
/* DWARF5 uses slot zero, but that is only set explicitly
using a .file 0 directive. If that isn't used, but dir
one is used, then use that as main file directory.
else
dir = remap_debug_filename (getpwd ());
- size = strlen (dir) + 1;
- cp = frag_more (size);
- memcpy (cp, dir, size);
+ line_strp = add_line_strp (line_str_seg, dir);
+ subseg_set (line_seg, 0);
+ TC_DWARF2_EMIT_OFFSET (line_strp, sizeof_offset);
}
for (i = 1; i < dirs_in_use; ++i)
{
dir = remap_debug_filename (dirs[i]);
- size = strlen (dir) + 1;
- cp = frag_more (size);
- memcpy (cp, dir, size);
+ if (DWARF2_LINE_VERSION < 5)
+ {
+ size = strlen (dir) + 1;
+ cp = frag_more (size);
+ memcpy (cp, dir, size);
+ }
+ else
+ {
+ line_strp = add_line_strp (line_str_seg, dir);
+ subseg_set (line_seg, 0);
+ TC_DWARF2_EMIT_OFFSET (line_strp, sizeof_offset);
+ }
}
if (DWARF2_LINE_VERSION < 5)
if (((unsigned long) DWARF2_FILE_TIME_NAME ("", "")) == -1UL)
{
- emit_timestamps = FALSE;
+ emit_timestamps = false;
-- columns;
}
if (DWARF2_FILE_SIZE_NAME ("", "") == -1)
{
- emit_filesize = FALSE;
+ emit_filesize = false;
-- columns;
}
break;
if (i < files_in_use)
{
- emit_md5 = TRUE;
+ emit_md5 = true;
++ columns;
}
out_byte (columns);
/* The format of the file name. */
out_uleb128 (DW_LNCT_path);
- /* FIXME: it would be better to store these strings in
- the .debug_line_str section and reference them here. */
- out_uleb128 (DW_FORM_string);
+ /* Store these strings in the .debug_line_str section so they
+ can be shared. */
+ out_uleb128 (DW_FORM_line_strp);
/* The format of the directory index. */
out_uleb128 (DW_LNCT_directory_index);
fullfilename = DWARF2_FILE_NAME (files[i].filename,
files[i].dir ? dirs [files [i].dir] : "");
- size = strlen (fullfilename) + 1;
- cp = frag_more (size);
- memcpy (cp, fullfilename, size);
+ if (DWARF2_LINE_VERSION < 5)
+ {
+ size = strlen (fullfilename) + 1;
+ cp = frag_more (size);
+ memcpy (cp, fullfilename, size);
+ }
+ else
+ {
+ line_strp = add_line_strp (line_str_seg, fullfilename);
+ subseg_set (line_seg, 0);
+ TC_DWARF2_EMIT_OFFSET (line_strp, sizeof_offset);
+ }
/* Directory number. */
out_uleb128 (files[i].dir);
matches up to the opcode base value we have been using. */
gas_assert (DWARF2_LINE_OPCODE_BASE == 13);
- out_dir_and_file_list ();
+ 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)
+ /* Paranoia - this check should have already have
+ been handled in dwarf2_gen_line_info_1(). */
if (SEG_NORMAL (s->seg))
process_entries (s->seg, s->head->head);
- else
- as_warn ("dwarf line number information for %s ignored",
- segment_name (s->seg));
if (flag_dwarf_sections)
/* We have to switch to the special .debug_line_end section
This section contains the line_end symbol which is used to
compute the size of the linked .debug_line section, as seen
in the DWARF Line Number header. */
- subseg_set (subseg_get (".debug_line_end", FALSE), 0);
+ subseg_set (subseg_get (".debug_line_end", false), 0);
symbol_set_value_now (line_end);
}
flag_dwarf_cie_version = 1;
}
-
/* Finish the dwarf2 debug sections. We emit .debug.line if there
- were any .file/.loc directives, or --gdwarf2 was given, or if the
+ were any .file/.loc directives, or --gdwarf2 was given, and if the
file has a non-empty .debug_info section and an empty .debug_line
section. If we emit .debug_line, and the .debug_info section is
empty, we also emit .debug_info, .debug_aranges and .debug_abbrev.
empty_debug_line = line_seg == NULL || !seg_not_empty_p (line_seg);
/* We can't construct a new debug_line section if we already have one.
- Give an error. */
- if (all_segs && !empty_debug_line)
+ Give an error if we have seen any .loc, otherwise trust the user
+ knows what they are doing and want to generate the .debug_line
+ (and all other debug sections) themselves. */
+ if (all_segs && !empty_debug_line && dwarf2_any_loc_directive_seen)
as_fatal ("duplicate .debug_line sections");
if ((!all_segs && emit_other_sections)
sizeof_address = DWARF2_ADDR_SIZE (stdoutput);
/* Create and switch to the line number section. */
- line_seg = subseg_new (".debug_line", 0);
- bfd_set_section_flags (line_seg, SEC_READONLY | SEC_DEBUGGING | SEC_OCTETS);
+ if (empty_debug_line)
+ {
+ line_seg = subseg_new (".debug_line", 0);
+ bfd_set_section_flags (line_seg,
+ SEC_READONLY | SEC_DEBUGGING | SEC_OCTETS);
+ }
/* For each subsection, chain the debug entries together. */
for (s = all_segs; s; s = s->next)
}
}
- out_debug_line (line_seg);
+ if (empty_debug_line)
+ out_debug_line (line_seg);
/* If this is assembler generated line info, and there is no
debug_info already, we need .debug_info, .debug_abbrev and