Add support for generating DWARF-5 format directory and file name tables from the assembler.

PR 25611
	PR 25614
	* dwarf.h (DWARF2_Internal_LineInfo): Add li_address_size and
	li_segment_size fields.
	* dwarf.c (read_debug_line_header): Record the address size and
	segment selector size values (if present) in the lineinfo
	structure.
	(display_formatted_table): Warn if the format count is empty but
	the table itself is not empty.
	Display the format count and entry count at the start of the table
	dump.
	(display_debug_lines_raw): Display the address size and segement
	selector size fields, if present.
	* testsuite/binutils-all/dw5.W: Update expected output.

gas	* dwarf2dbg.c (DWARF2_FILE_TIME_NAME): Default to -1.
	(DWARF2_FILE_SIZE_NAME): Default to -1.
	(DWARF2_LINE_VERSION): Default to the current dwarf level or 3,
	whichever is higher.
	(DWARF2_LINE_MAX_OPS_PER_INSN): Provide a default value of 1.
	(NUM_MD5_BYTES): Define.
	(struct file entry): Add md5 field.
	(get_filenum): Delete and replace with...
	(get_basename): New function.
	(get_directory_table_entry): New function.
	(allocate_filenum): New function.
	(allocate_filename_to_slot): New function.
	(dwarf2_where): Use new functions.
	(dwarf2_directive_filename): Add support for extended .file
	pseudo-op.
	(dwarf2_directive_loc): Allow the use of file number zero with
	DWARF 5 or higher.
	(out_file_list): Rename to...
	(out_dir_and_file_list): Add DWARF 5 support.
	(out_debug_line): Emit extra values into the section header for
	DWARF 5.
	(out_debug_str): Allow for file 0 to be used with DWARF 5.
	* doc/as.texi (.file): Update the description of this pseudo-op.
	* testsuite/gas/elf-dwarf-5-file0.s: Add more lines.
	* testsuite/gas/elf-dwarf-5-file0.d: Update expected dump output.
	* testsuite/gas/lns/lns-diag-1.l: Update expected error message.
	* NEWS: Mention the new feature.
This commit is contained in:
Nick Clifton
2020-03-11 10:17:14 +00:00
parent b76f3a4237
commit 5496f3c635
11 changed files with 672 additions and 196 deletions

View File

@@ -1,3 +1,20 @@
2020-03-11 Nick Clifton <nickc@redhat.com>
PR 25611
PR 25614
* dwarf.h (DWARF2_Internal_LineInfo): Add li_address_size and
li_segment_size fields.
* dwarf.c (read_debug_line_header): Record the address size and
segment selector size values (if present) in the lineinfo
structure.
(display_formatted_table): Warn if the format count is empty but
the table itself is not empty.
Display the format count and entry count at the start of the table
dump.
(display_debug_lines_raw): Display the address size and segement
selector size fields, if present.
* testsuite/binutils-all/dw5.W: Update expected output.
2020-03-11 Alan Modra <amodra@gmail.com> 2020-03-11 Alan Modra <amodra@gmail.com>
PR 25651 PR 25651

View File

@@ -3622,7 +3622,6 @@ read_debug_line_header (struct dwarf_section * section,
{ {
unsigned char *hdrptr; unsigned char *hdrptr;
unsigned int initial_length_size; unsigned int initial_length_size;
unsigned char address_size, segment_selector_size;
/* Extract information from the Line Number Program Header. /* Extract information from the Line Number Program Header.
(section 6.2.4 in the Dwarf3 doc). */ (section 6.2.4 in the Dwarf3 doc). */
@@ -3679,15 +3678,15 @@ read_debug_line_header (struct dwarf_section * section,
if (linfo->li_version >= 5) if (linfo->li_version >= 5)
{ {
SAFE_BYTE_GET_AND_INC (address_size, hdrptr, 1, end); SAFE_BYTE_GET_AND_INC (linfo->li_address_size, hdrptr, 1, end);
SAFE_BYTE_GET_AND_INC (segment_selector_size, hdrptr, 1, end); SAFE_BYTE_GET_AND_INC (linfo->li_segment_size, hdrptr, 1, end);
if (segment_selector_size != 0) if (linfo->li_segment_size != 0)
{ {
warn (_("The %s section contains " warn (_("The %s section contains "
"unsupported segment selector size: %d.\n"), "unsupported segment selector size: %d.\n"),
section->name, segment_selector_size); section->name, linfo->li_segment_size);
return 0; return NULL;
} }
} }
@@ -3737,6 +3736,7 @@ display_formatted_table (unsigned char * data,
unsigned char *format_start, format_count, *format, formati; unsigned char *format_start, format_count, *format, formati;
dwarf_vma data_count, datai; dwarf_vma data_count, datai;
unsigned int namepass, last_entry = 0; unsigned int namepass, last_entry = 0;
const char * table_name = is_dir ? N_("Directory Table") : N_("File Name Table");
SAFE_BYTE_GET_AND_INC (format_count, data, 1, end); SAFE_BYTE_GET_AND_INC (format_count, data, 1, end);
format_start = data; format_start = data;
@@ -3746,10 +3746,7 @@ display_formatted_table (unsigned char * data,
SKIP_ULEB (data, end); SKIP_ULEB (data, end);
if (data == end) if (data == end)
{ {
if (is_dir) warn (_("%s: Corrupt format description entry\n"), table_name);
warn (_("Corrupt directory format table entry\n"));
else
warn (_("Corrupt file name format table entry\n"));
return data; return data;
} }
} }
@@ -3757,28 +3754,25 @@ display_formatted_table (unsigned char * data,
READ_ULEB (data_count, data, end); READ_ULEB (data_count, data, end);
if (data == end) if (data == end)
{ {
if (is_dir) warn (_("%s: Corrupt entry count\n"), table_name);
warn (_("Corrupt directory list\n"));
else
warn (_("Corrupt file name list\n"));
return data; return data;
} }
if (data_count == 0) if (data_count == 0)
{ {
if (is_dir) printf (_("\n The %s is empty.\n"), table_name);
printf (_("\n The Directory Table is empty.\n"));
else
printf (_("\n The File Name Table is empty.\n"));
return data; return data;
} }
else if (format_count == 0)
{
warn (_("%s: format count is zero, but the table is not empty\n"),
table_name);
return end;
}
if (is_dir) printf (_("\n The %s (offset 0x%lx, lines %s, columns %u):\n"),
printf (_("\n The Directory Table (offset 0x%lx):\n"), table_name, (long) (data - start), dwarf_vmatoa ("u", data_count),
(long) (data - start)); format_count);
else
printf (_("\n The File Name Table (offset 0x%lx):\n"),
(long) (data - start));
printf (_(" Entry")); printf (_(" Entry"));
/* Delay displaying name as the last entry for better screen layout. */ /* Delay displaying name as the last entry for better screen layout. */
@@ -3806,7 +3800,7 @@ display_formatted_table (unsigned char * data,
printf (_("\tSize")); printf (_("\tSize"));
break; break;
case DW_LNCT_MD5: case DW_LNCT_MD5:
printf (_("\tMD5")); printf (_("\tMD5\t\t\t"));
break; break;
default: default:
printf (_("\t(Unknown format content type %s)"), printf (_("\t(Unknown format content type %s)"),
@@ -3840,12 +3834,10 @@ display_formatted_table (unsigned char * data,
section, NULL, '\t', -1); section, NULL, '\t', -1);
} }
} }
if (data == end)
if (data == end && (datai < data_count - 1))
{ {
if (is_dir) warn (_("\n%s: Corrupt entries list\n"), table_name);
warn (_("Corrupt directory entries list\n"));
else
warn (_("Corrupt file name entries list\n"));
return data; return data;
} }
putchar ('\n'); putchar ('\n');
@@ -3909,6 +3901,11 @@ display_debug_lines_raw (struct dwarf_section * section,
printf (_(" Offset: 0x%lx\n"), (long)(data - start)); printf (_(" Offset: 0x%lx\n"), (long)(data - start));
printf (_(" Length: %ld\n"), (long) linfo.li_length); printf (_(" Length: %ld\n"), (long) linfo.li_length);
printf (_(" DWARF Version: %d\n"), linfo.li_version); printf (_(" DWARF Version: %d\n"), linfo.li_version);
if (linfo.li_version >= 5)
{
printf (_(" Address size (bytes): %d\n"), linfo.li_address_size);
printf (_(" Segment selector (bytes): %d\n"), linfo.li_segment_size);
}
printf (_(" Prologue Length: %d\n"), (int) linfo.li_prologue_length); printf (_(" Prologue Length: %d\n"), (int) linfo.li_prologue_length);
printf (_(" Minimum Instruction Length: %d\n"), linfo.li_min_insn_length); printf (_(" Minimum Instruction Length: %d\n"), linfo.li_min_insn_length);
if (linfo.li_version >= 4) if (linfo.li_version >= 4)

View File

@@ -29,6 +29,8 @@ typedef struct
{ {
dwarf_vma li_length; dwarf_vma li_length;
unsigned short li_version; unsigned short li_version;
unsigned char li_address_size;
unsigned char li_segment_size;
dwarf_vma li_prologue_length; dwarf_vma li_prologue_length;
unsigned char li_min_insn_length; unsigned char li_min_insn_length;
unsigned char li_max_ops_per_insn; unsigned char li_max_ops_per_insn;

View File

@@ -291,6 +291,8 @@ Raw dump of debug contents of section .debug_line:
Offset: 0x0 Offset: 0x0
Length: 144 Length: 144
DWARF Version: 5 DWARF Version: 5
Address size \(bytes\): 8
Segment selector \(bytes\): 0
Prologue Length: 60 Prologue Length: 60
Minimum Instruction Length: 1 Minimum Instruction Length: 1
Maximum Ops per Instruction: 1 Maximum Ops per Instruction: 1
@@ -313,13 +315,13 @@ Raw dump of debug contents of section .debug_line:
Opcode 11 has 0 args Opcode 11 has 0 args
Opcode 12 has 1 arg Opcode 12 has 1 arg
The Directory Table \(offset 0x22\): The Directory Table \(offset 0x22, lines 3, columns 1\):
Entry Name Entry Name
0 \(indirect line string, offset: 0x0\): 0 \(indirect line string, offset: 0x0\):
1 \(indirect line string, offset: 0x1\): 1 \(indirect line string, offset: 0x1\):
2 \(indirect line string, offset: 0x22\): /usr/include 2 \(indirect line string, offset: 0x22\): /usr/include
The File Name Table \(offset 0x34\): The File Name Table \(offset 0x34, lines 4, columns 2\):
Entry Dir Name Entry Dir Name
0 0 \(indirect line string, offset: 0x14\): main.c 0 0 \(indirect line string, offset: 0x14\): main.c
1 1 \(indirect line string, offset: 0x1b\): main.c 1 1 \(indirect line string, offset: 0x1b\): main.c

View File

@@ -1,3 +1,35 @@
2020-03-11 Nick Clifton <nickc@redhat.com>
PR 25611
PR 25614
* dwarf2dbg.c (DWARF2_FILE_TIME_NAME): Default to -1.
(DWARF2_FILE_SIZE_NAME): Default to -1.
(DWARF2_LINE_VERSION): Default to the current dwarf level or 3,
whichever is higher.
(DWARF2_LINE_MAX_OPS_PER_INSN): Provide a default value of 1.
(NUM_MD5_BYTES): Define.
(struct file entry): Add md5 field.
(get_filenum): Delete and replace with...
(get_basename): New function.
(get_directory_table_entry): New function.
(allocate_filenum): New function.
(allocate_filename_to_slot): New function.
(dwarf2_where): Use new functions.
(dwarf2_directive_filename): Add support for extended .file
pseudo-op.
(dwarf2_directive_loc): Allow the use of file number zero with
DWARF 5 or higher.
(out_file_list): Rename to...
(out_dir_and_file_list): Add DWARF 5 support.
(out_debug_line): Emit extra values into the section header for
DWARF 5.
(out_debug_str): Allow for file 0 to be used with DWARF 5.
* doc/as.texi (.file): Update the description of this pseudo-op.
* testsuite/gas/elf-dwarf-5-file0.s: Add more lines.
* testsuite/gas/elf-dwarf-5-file0.d: Update expected dump output.
* testsuite/gas/lns/lns-diag-1.l: Update expected error message.
* NEWS: Mention the new feature.
2020-03-10 Alan Modra <amodra@gmail.com> 2020-03-10 Alan Modra <amodra@gmail.com>
* config/tc-csky.c (get_operand_value): Rewrite 1 << 31 expressions * config/tc-csky.c (get_operand_value): Rewrite 1 << 31 expressions

View File

@@ -1,5 +1,9 @@
-*- text -*- -*- text -*-
* Add --gdwarf-5 option to the assembler to generate DWARF 5 debug output
(if such output is being generated). Added the ability to generate
version 5 .debug_line sections.
Changes in 2.34: Changes in 2.34:
* Add -malign-branch-boundary=NUM, -malign-branch=TYPE[+TYPE...], * Add -malign-branch-boundary=NUM, -malign-branch=TYPE[+TYPE...],

View File

@@ -5366,13 +5366,29 @@ to the @code{.debug_line} file name table. The syntax is:
The @var{fileno} operand should be a unique positive integer to use as the The @var{fileno} operand should be a unique positive integer to use as the
index of the entry in the table. The @var{filename} operand is a C string index of the entry in the table. The @var{filename} operand is a C string
literal. literal enclosed in double quotes. The @var{filename} can include directory
elements. If it does, then the directory will be added to the directory table
and the basename will be added to the file table.
The detail of filename indices is exposed to the user because the filename The detail of filename indices is exposed to the user because the filename
table is shared with the @code{.debug_info} section of the DWARF2 debugging table is shared with the @code{.debug_info} section of the DWARF2 debugging
information, and thus the user must know the exact indices that table information, and thus the user must know the exact indices that table
entries will have. entries will have.
If DWARF-5 support has been enabled via the @option{-gdwarf-5} option then
an extended version of the @code{file} is also allowed:
@smallexample
.file @var{fileno} [@var{dirname}] @var{filename} [md5 @var{value}]
@end smallexample
With this version a separate directory name is allowed, although if this is
used then @var{filename} should not contain any directory components. In
addtion an md5 hash value of the contents of @var{filename} can be provided.
This will be stored in the the file table as well, and can be used by tools
reading the debug information to verify that the contents of the source file
match the contents of the compiled file.
@node Fill @node Fill
@section @code{.fill @var{repeat} , @var{size} , @var{value}} @section @code{.fill @var{repeat} , @var{size} , @var{value}}

View File

@@ -30,6 +30,7 @@
#include "as.h" #include "as.h"
#include "safe-ctype.h" #include "safe-ctype.h"
#include "bignum.h"
#ifdef HAVE_LIMITS_H #ifdef HAVE_LIMITS_H
#include <limits.h> #include <limits.h>
@@ -81,11 +82,11 @@
#endif #endif
#ifndef DWARF2_FILE_TIME_NAME #ifndef DWARF2_FILE_TIME_NAME
#define DWARF2_FILE_TIME_NAME(FILENAME,DIRNAME) 0 #define DWARF2_FILE_TIME_NAME(FILENAME,DIRNAME) -1
#endif #endif
#ifndef DWARF2_FILE_SIZE_NAME #ifndef DWARF2_FILE_SIZE_NAME
#define DWARF2_FILE_SIZE_NAME(FILENAME,DIRNAME) 0 #define DWARF2_FILE_SIZE_NAME(FILENAME,DIRNAME) -1
#endif #endif
#ifndef DWARF2_VERSION #ifndef DWARF2_VERSION
@@ -99,7 +100,7 @@
/* This implementation outputs version 3 .debug_line information. */ /* This implementation outputs version 3 .debug_line information. */
#ifndef DWARF2_LINE_VERSION #ifndef DWARF2_LINE_VERSION
#define DWARF2_LINE_VERSION 3 #define DWARF2_LINE_VERSION (dwarf_level > 3 ? dwarf_level : 3)
#endif #endif
#include "subsegs.h" #include "subsegs.h"
@@ -147,6 +148,10 @@
/* Flag that indicates the initial value of the is_stmt_start flag. */ /* Flag that indicates the initial value of the is_stmt_start flag. */
#define DWARF2_LINE_DEFAULT_IS_STMT 1 #define DWARF2_LINE_DEFAULT_IS_STMT 1
#ifndef DWARF2_LINE_MAX_OPS_PER_INSN
#define DWARF2_LINE_MAX_OPS_PER_INSN 1
#endif
/* Given a special op, return the line skip amount. */ /* Given a special op, return the line skip amount. */
#define SPECIAL_LINE(op) \ #define SPECIAL_LINE(op) \
(((op) - DWARF2_LINE_OPCODE_BASE)%DWARF2_LINE_RANGE + DWARF2_LINE_BASE) (((op) - DWARF2_LINE_OPCODE_BASE)%DWARF2_LINE_RANGE + DWARF2_LINE_BASE)
@@ -196,10 +201,13 @@ struct line_seg
static struct line_seg *all_segs; static struct line_seg *all_segs;
static struct line_seg **last_seg_ptr; static struct line_seg **last_seg_ptr;
#define NUM_MD5_BYTES 16
struct file_entry struct file_entry
{ {
const char *filename; const char * filename;
unsigned int dir; unsigned int dir;
unsigned char md5[NUM_MD5_BYTES];
}; };
/* Table of files used by .debug_line. */ /* Table of files used by .debug_line. */
@@ -208,9 +216,9 @@ static unsigned int files_in_use;
static unsigned int files_allocated; static unsigned int files_allocated;
/* Table of directories used by .debug_line. */ /* Table of directories used by .debug_line. */
static char **dirs; static char ** dirs = NULL;
static unsigned int dirs_in_use; static unsigned int dirs_in_use = 0;
static unsigned int dirs_allocated; static unsigned int dirs_allocated = 0;
/* TRUE when we've seen a .loc directive recently. Used to avoid /* TRUE when we've seen a .loc directive recently. Used to avoid
doing work when there's nothing to do. */ doing work when there's nothing to do. */
@@ -239,8 +247,6 @@ static symbolS *view_assert_failed;
/* The size of an address on the target. */ /* The size of an address on the target. */
static unsigned int sizeof_address; static unsigned int sizeof_address;
static unsigned int get_filenum (const char *, unsigned int);
#ifndef TC_DWARF2_EMIT_OFFSET #ifndef TC_DWARF2_EMIT_OFFSET
#define TC_DWARF2_EMIT_OFFSET generic_dwarf2_emit_offset #define TC_DWARF2_EMIT_OFFSET generic_dwarf2_emit_offset
@@ -280,6 +286,7 @@ get_line_subseg (segT seg, subsegT subseg, bfd_boolean create_p)
last_seg_ptr = &s->next; last_seg_ptr = &s->next;
seg_info (seg)->dwarf2_line_seg = s; seg_info (seg)->dwarf2_line_seg = s;
} }
gas_assert (seg == s->seg); gas_assert (seg == s->seg);
for (pss = &s->head; (lss = *pss) != NULL ; pss = &lss->next) for (pss = &s->head; (lss = *pss) != NULL ; pss = &lss->next)
@@ -516,7 +523,9 @@ dwarf2_gen_line_info (addressT ofs, struct dwarf2_line_info *loc)
symbolS *sym; symbolS *sym;
/* Early out for as-yet incomplete location information. */ /* Early out for as-yet incomplete location information. */
if (loc->filenum == 0 || loc->line == 0) if (loc->line == 0)
return;
if (loc->filenum == 0 && DWARF2_LINE_VERSION < 5)
return; return;
/* Don't emit sequences of line symbols for the same line when the /* Don't emit sequences of line symbols for the same line when the
@@ -544,6 +553,323 @@ dwarf2_gen_line_info (addressT ofs, struct dwarf2_line_info *loc)
dwarf2_gen_line_info_1 (sym, loc); dwarf2_gen_line_info_1 (sym, loc);
} }
static const char *
get_basename (const char * pathname)
{
const char * file;
file = lbasename (pathname);
/* Don't make empty string from / or A: from A:/ . */
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
if (file <= pathname + 3)
file = pathname;
#else
if (file == pathname + 1)
file = pathname;
#endif
return file;
}
static unsigned int
get_directory_table_entry (const char * dirname,
size_t dirlen,
bfd_boolean can_use_zero)
{
unsigned int d;
if (dirlen == 0)
return 0;
#ifndef DWARF2_DIR_SHOULD_END_WITH_SEPARATOR
if (IS_DIR_SEPARATOR (dirname[dirlen - 1]))
{
-- dirlen;
if (dirlen == 0)
return 0;
}
#endif
for (d = 0; d < dirs_in_use; ++d)
{
if (dirs[d] != NULL
&& filename_ncmp (dirname, dirs[d], dirlen) == 0
&& dirs[d][dirlen] == '\0')
return d;
}
if (can_use_zero)
{
if (dirs == NULL || dirs[0] == NULL)
d = 0;
}
else if (d == 0)
d = 1;
if (d >= dirs_allocated)
{
unsigned int old = dirs_allocated;
dirs_allocated = d + 32;
dirs = XRESIZEVEC (char *, dirs, dirs_allocated);
memset (dirs + old, 0, (dirs_allocated - old) * sizeof (char *));
}
dirs[d] = xmemdup0 (dirname, dirlen);
if (dirs_in_use <= d)
dirs_in_use = d + 1;
return d;
}
/* Get a .debug_line file number for PATHNAME. If there is a
directory component to PATHNAME, then this will be stored
in the directory table, if it is not already present.
Returns the slot number allocated to that filename or -1
if there was a problem. */
static signed int
allocate_filenum (const char * pathname)
{
static signed int last_used = -1, last_used_dir_len = 0;
const char *file;
size_t dir_len;
unsigned int i, dir;
/* Short circuit the common case of adding the same pathname
as last time. */
if (last_used != -1)
{
const char * dirname = NULL;
if (dirs != NULL)
dirname = dirs[files[last_used].dir];
if (dirname == NULL)
{
if (filename_cmp (pathname, files[last_used].filename) == 0)
return last_used;
}
else
{
if (filename_ncmp (pathname, dirname, last_used_dir_len) == 0
&& IS_DIR_SEPARATOR (pathname [last_used_dir_len])
&& filename_cmp (pathname + last_used_dir_len + 1,
files[last_used].filename) == 0)
return last_used;
}
}
file = get_basename (pathname);
dir_len = file - pathname;
dir = get_directory_table_entry (pathname, dir_len, FALSE);
/* Do not use slot-0. That is specificailly reserved for use by
the '.file 0 "name"' directive. */
for (i = 1; i < files_in_use; ++i)
if (files[i].dir == dir
&& files[i].filename
&& filename_cmp (file, files[i].filename) == 0)
{
last_used = i;
last_used_dir_len = dir_len;
return i;
}
if (i >= files_allocated)
{
unsigned int old = files_allocated;
files_allocated = i + 32;
/* Catch wraparound. */
if (files_allocated <= old)
{
as_bad (_("file number %lu is too big"), (unsigned long) i);
return -1;
}
files = XRESIZEVEC (struct file_entry, files, files_allocated);
memset (files + old, 0, (i + 32 - old) * sizeof (struct file_entry));
}
files[i].filename = file;
files[i].dir = dir;
memset (files[i].md5, 0, NUM_MD5_BYTES);
if (files_in_use < i + 1)
files_in_use = i + 1;
last_used = i;
last_used_dir_len = dir_len;
return i;
}
/* Allocate slot NUM in the .debug_line file table to FILENAME.
If DIRNAME is not NULL or there is a directory component to FILENAME
then this will be stored in the directory table, if not already present.
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)
{
const char *file;
size_t dirlen;
unsigned int i, d;
/* Short circuit the common case of adding the same pathname
as last time. */
if (num < files_allocated && files[num].filename != NULL)
{
const char * dir = NULL;
if (dirs)
dir = dirs[files[num].dir];
if (with_md5
&& memcmp (generic_bignum, files[num].md5, NUM_MD5_BYTES) != 0)
goto fail;
if (dirname != NULL)
{
if (dir != NULL && filename_cmp (dir, dirname) != 0)
goto fail;
if (filename_cmp (filename, files[num].filename) != 0)
goto fail;
/* 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));
return TRUE;
}
else if (dir != NULL)
{
dirlen = strlen (dir);
if (filename_ncmp (filename, dir, dirlen) == 0
&& IS_DIR_SEPARATOR (filename [dirlen])
&& filename_cmp (filename + dirlen + 1, files[num].filename) == 0)
return TRUE;
}
else /* dir == NULL */
{
file = get_basename (filename);
if (filename_cmp (file, files[num].filename) == 0)
{
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;
}
}
fail:
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)
{
dirname = filename;
file = get_basename (filename);
dirlen = file - filename;
}
else
{
dirlen = strlen (dirname);
file = filename;
}
d = get_directory_table_entry (dirname, dirlen, num == 0);
i = num;
if (i >= files_allocated)
{
unsigned int old = files_allocated;
files_allocated = i + 32;
/* Catch wraparound. */
if (files_allocated <= old)
{
as_bad (_("file number %lu is too big"), (unsigned long) i);
return FALSE;
}
files = XRESIZEVEC (struct file_entry, files, files_allocated);
memset (files + old, 0, (i + 32 - old) * sizeof (struct file_entry));
}
files[i].filename = file;
files[i].dir = d;
if (with_md5)
{
if (target_big_endian)
{
/* md5's are stored in litte endian format. */
unsigned int bits_remaining = NUM_MD5_BYTES * BITS_PER_CHAR;
unsigned int byte = NUM_MD5_BYTES;
unsigned int bignum_index = 0;
while (bits_remaining)
{
unsigned int bignum_bits_remaining = LITTLENUM_NUMBER_OF_BITS;
valueT bignum_value = generic_bignum [bignum_index];
bignum_index ++;
while (bignum_bits_remaining)
{
files[i].md5[--byte] = bignum_value & 0xff;
bignum_value >>= 8;
bignum_bits_remaining -= 8;
bits_remaining -= 8;
}
}
}
else
{
unsigned int bits_remaining = NUM_MD5_BYTES * BITS_PER_CHAR;
unsigned int byte = 0;
unsigned int bignum_index = 0;
while (bits_remaining)
{
unsigned int bignum_bits_remaining = LITTLENUM_NUMBER_OF_BITS;
valueT bignum_value = generic_bignum [bignum_index];
bignum_index ++;
while (bignum_bits_remaining)
{
files[i].md5[byte++] = bignum_value & 0xff;
bignum_value >>= 8;
bignum_bits_remaining -= 8;
bits_remaining -= 8;
}
}
}
}
else
memset (files[i].md5, 0, NUM_MD5_BYTES);
if (files_in_use < i + 1)
files_in_use = i + 1;
return TRUE;
}
/* Returns the current source information. If .file directives have /* Returns the current source information. If .file directives have
been encountered, the info for the corresponding source file is been encountered, the info for the corresponding source file is
returned. Otherwise, the info for the assembly source file is returned. Otherwise, the info for the assembly source file is
@@ -558,7 +884,8 @@ dwarf2_where (struct dwarf2_line_info *line)
memset (line, 0, sizeof (*line)); memset (line, 0, sizeof (*line));
filename = as_where (&line->line); filename = as_where (&line->line);
line->filenum = get_filenum (filename, 0); line->filenum = allocate_filenum (filename);
/* FIXME: We should check the return value from allocate_filenum. */
line->column = 0; line->column = 0;
line->flags = DWARF2_FLAG_IS_STMT; line->flags = DWARF2_FLAG_IS_STMT;
line->isa = current.isa; line->isa = current.isa;
@@ -670,108 +997,6 @@ dwarf2_emit_label (symbolS *label)
dwarf2_consume_line_info (); dwarf2_consume_line_info ();
} }
/* Get a .debug_line file number for FILENAME. If NUM is nonzero,
allocate it on that file table slot, otherwise return the first
empty one. */
static unsigned int
get_filenum (const char *filename, unsigned int num)
{
static unsigned int last_used, last_used_dir_len;
const char *file;
size_t dir_len;
unsigned int i, dir;
if (num == 0 && last_used)
{
if (! files[last_used].dir
&& filename_cmp (filename, files[last_used].filename) == 0)
return last_used;
if (files[last_used].dir
&& filename_ncmp (filename, dirs[files[last_used].dir],
last_used_dir_len) == 0
&& IS_DIR_SEPARATOR (filename [last_used_dir_len])
&& filename_cmp (filename + last_used_dir_len + 1,
files[last_used].filename) == 0)
return last_used;
}
file = lbasename (filename);
/* Don't make empty string from / or A: from A:/ . */
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
if (file <= filename + 3)
file = filename;
#else
if (file == filename + 1)
file = filename;
#endif
dir_len = file - filename;
dir = 0;
if (dir_len)
{
#ifndef DWARF2_DIR_SHOULD_END_WITH_SEPARATOR
--dir_len;
#endif
for (dir = 1; dir < dirs_in_use; ++dir)
if (filename_ncmp (filename, dirs[dir], dir_len) == 0
&& dirs[dir][dir_len] == '\0')
break;
if (dir >= dirs_in_use)
{
if (dir >= dirs_allocated)
{
dirs_allocated = dir + 32;
dirs = XRESIZEVEC (char *, dirs, dirs_allocated);
}
dirs[dir] = xmemdup0 (filename, dir_len);
dirs_in_use = dir + 1;
}
}
if (num == 0)
{
for (i = 1; i < files_in_use; ++i)
if (files[i].dir == dir
&& files[i].filename
&& filename_cmp (file, files[i].filename) == 0)
{
last_used = i;
last_used_dir_len = dir_len;
return i;
}
}
else
i = num;
if (i >= files_allocated)
{
unsigned int old = files_allocated;
files_allocated = i + 32;
/* Catch wraparound. */
if (files_allocated <= old)
{
as_bad (_("file number %lu is too big"), (unsigned long) i);
return 0;
}
files = XRESIZEVEC (struct file_entry, files, files_allocated);
memset (files + old, 0, (i + 32 - old) * sizeof (struct file_entry));
}
files[i].filename = file;
files[i].dir = dir;
if (files_in_use < i + 1)
files_in_use = i + 1;
last_used = i;
last_used_dir_len = dir_len;
return i;
}
/* Handle two forms of .file directive: /* Handle two forms of .file directive:
- Pass .file "source.c" to s_app_file - Pass .file "source.c" to s_app_file
- Handle .file 1 "source.c" by adding an entry to the DWARF-2 file table - Handle .file 1 "source.c" by adding an entry to the DWARF-2 file table
@@ -781,8 +1006,10 @@ get_filenum (const char *filename, unsigned int num)
char * char *
dwarf2_directive_filename (void) dwarf2_directive_filename (void)
{ {
bfd_boolean with_md5 = TRUE;
valueT num; valueT num;
char *filename; char *filename;
const char * dirname = NULL;
int filename_len; int filename_len;
/* Continue to accept a bare string and pass it off. */ /* Continue to accept a bare string and pass it off. */
@@ -795,24 +1022,48 @@ dwarf2_directive_filename (void)
num = get_absolute_expression (); num = get_absolute_expression ();
if ((offsetT) num < 1 && dwarf_level < 5) if ((offsetT) num < 1 && DWARF2_LINE_VERSION < 5)
{ {
as_bad (_("file number less than one")); as_bad (_("file number less than one"));
ignore_rest_of_line (); ignore_rest_of_line ();
return NULL; return NULL;
} }
if (num == 0) /* FIXME: Should we allow ".file <N>\n" as an expression meaning
{ "switch back to the already allocated file <N> as the current
demand_empty_rest_of_line (); file" ? */
return NULL;
}
filename = demand_copy_C_string (&filename_len); filename = demand_copy_C_string (&filename_len);
if (filename == NULL) if (filename == NULL)
/* demand_copy_C_string will have already generated an error message. */ /* demand_copy_C_string will have already generated an error message. */
return NULL; return NULL;
/* For DWARF-5 support we also accept:
.file <NUM> ["<dir>"] "<file>" [md5 <NUM>] */
if (DWARF2_LINE_VERSION > 4)
{
SKIP_WHITESPACE ();
if (*input_line_pointer == '"')
{
dirname = filename;
filename = demand_copy_C_string (&filename_len);
SKIP_WHITESPACE ();
}
if (strncmp (input_line_pointer, "md5", 3) == 0)
{
input_line_pointer += 3;
SKIP_WHITESPACE ();
expressionS exp;
expression_and_evaluate (& exp);
if (exp.X_op != O_big)
as_bad (_("md5 value too small or not a constant"));
else
with_md5 = TRUE;
}
}
demand_empty_rest_of_line (); demand_empty_rest_of_line ();
/* A .file directive implies compiler generated debug information is /* A .file directive implies compiler generated debug information is
@@ -825,13 +1076,10 @@ dwarf2_directive_filename (void)
as_bad (_("file number %lu is too big"), (unsigned long) num); as_bad (_("file number %lu is too big"), (unsigned long) num);
return NULL; return NULL;
} }
if (num < files_in_use && files[num].filename != 0)
{
as_bad (_("file number %u already allocated"), (unsigned int) num);
return NULL;
}
(void) get_filenum (filename, (unsigned int) num); if (! allocate_filename_to_slot (dirname, filename, (unsigned int) num,
with_md5))
return NULL;
return filename; return filename;
} }
@@ -860,11 +1108,15 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
line = get_absolute_expression (); line = get_absolute_expression ();
if (filenum < 1) if (filenum < 1)
{
if (filenum != 0 || DWARF2_LINE_VERSION < 5)
{ {
as_bad (_("file number less than one")); as_bad (_("file number less than one"));
return; return;
} }
if (filenum >= (int) files_in_use || files[filenum].filename == 0) }
if (filenum >= (int) files_in_use || files[filenum].filename == NULL)
{ {
as_bad (_("unassigned file number %ld"), (long) filenum); as_bad (_("unassigned file number %ld"), (long) filenum);
return; return;
@@ -1710,14 +1962,40 @@ process_entries (segT seg, struct line_entry *e)
/* Emit the directory and file tables for .debug_line. */ /* Emit the directory and file tables for .debug_line. */
static void static void
out_file_list (void) out_dir_and_file_list (void)
{ {
size_t size; size_t size;
const char *dir; const char *dir;
char *cp; char *cp;
unsigned int i; unsigned int i;
bfd_boolean emit_md5 = FALSE;
bfd_boolean emit_timestamps = TRUE;
bfd_boolean emit_filesize = TRUE;
/* Output the Directory Table. */
if (DWARF2_LINE_VERSION >= 5)
{
out_byte (1);
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);
out_uleb128 (dirs_in_use);
}
/* Emit directory list. */ /* Emit directory list. */
if (DWARF2_LINE_VERSION >= 5)
{
if (dirs[0] == NULL)
dir = remap_debug_filename (".");
else
dir = remap_debug_filename (dirs[0]);
size = strlen (dir) + 1;
cp = frag_more (size);
memcpy (cp, dir, size);
}
for (i = 1; i < dirs_in_use; ++i) for (i = 1; i < dirs_in_use; ++i)
{ {
dir = remap_debug_filename (dirs[i]); dir = remap_debug_filename (dirs[i]);
@@ -1725,20 +2003,90 @@ out_file_list (void)
cp = frag_more (size); cp = frag_more (size);
memcpy (cp, dir, size); memcpy (cp, dir, size);
} }
if (DWARF2_LINE_VERSION < 5)
/* Terminate it. */ /* Terminate it. */
out_byte ('\0'); out_byte ('\0');
for (i = 1; i < files_in_use; ++i) /* Output the File Name Table. */
if (DWARF2_LINE_VERSION >= 5)
{
unsigned int columns = 4;
if (((unsigned long) DWARF2_FILE_TIME_NAME ("", "")) == -1UL)
{
emit_timestamps = FALSE;
-- columns;
}
if (DWARF2_FILE_SIZE_NAME ("", "") == -1)
{
emit_filesize = FALSE;
-- columns;
}
for (i = 0; i < files_in_use; ++i)
if (files[i].md5[0] != 0)
break;
if (i < files_in_use)
{
emit_md5 = TRUE;
++ columns;
}
/* The number of format entries to follow. */
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);
/* The format of the directory index. */
out_uleb128 (DW_LNCT_directory_index);
out_uleb128 (DW_FORM_udata);
if (emit_timestamps)
{
/* The format of the timestamp. */
out_uleb128 (DW_LNCT_timestamp);
out_uleb128 (DW_FORM_udata);
}
if (emit_filesize)
{
/* The format of the file size. */
out_uleb128 (DW_LNCT_size);
out_uleb128 (DW_FORM_udata);
}
if (emit_md5)
{
/* The format of the MD5 sum. */
out_uleb128 (DW_LNCT_MD5);
out_uleb128 (DW_FORM_data16);
}
/* The number of entries in the table. */
out_uleb128 (files_in_use);
}
for (i = DWARF2_LINE_VERSION > 4 ? 0 : 1; i < files_in_use; ++i)
{ {
const char *fullfilename; const char *fullfilename;
if (files[i].filename == NULL) if (files[i].filename == NULL)
{ {
as_bad (_("unassigned file number %ld"), (long) i);
/* Prevent a crash later, particularly for file 1. */ /* Prevent a crash later, particularly for file 1. */
files[i].filename = ""; files[i].filename = "";
if (DWARF2_LINE_VERSION < 5 || i != 0)
{
as_bad (_("unassigned file number %ld"), (long) i);
continue; continue;
} }
}
fullfilename = DWARF2_FILE_NAME (files[i].filename, fullfilename = DWARF2_FILE_NAME (files[i].filename,
files[i].dir ? dirs [files [i].dir] : ""); files[i].dir ? dirs [files [i].dir] : "");
@@ -1746,15 +2094,43 @@ out_file_list (void)
cp = frag_more (size); cp = frag_more (size);
memcpy (cp, fullfilename, size); memcpy (cp, fullfilename, size);
out_uleb128 (files[i].dir); /* directory number */ /* Directory number. */
out_uleb128 (files[i].dir);
/* Output the last modification timestamp. */ /* Output the last modification timestamp. */
out_uleb128 (DWARF2_FILE_TIME_NAME (files[i].filename, if (emit_timestamps)
files[i].dir ? dirs [files [i].dir] : "")); {
/* Output the filesize. */ offsetT timestamp;
out_uleb128 (DWARF2_FILE_SIZE_NAME (files[i].filename,
files[i].dir ? dirs [files [i].dir] : "")); timestamp = DWARF2_FILE_TIME_NAME (files[i].filename,
files[i].dir ? dirs [files [i].dir] : "");
if (timestamp == -1)
timestamp = 0;
out_uleb128 (timestamp);
} }
/* Output the filesize. */
if (emit_filesize)
{
offsetT filesize;
filesize = DWARF2_FILE_SIZE_NAME (files[i].filename,
files[i].dir ? dirs [files [i].dir] : "");
if (filesize == -1)
filesize = 0;
out_uleb128 (filesize);
}
/* Output the md5 sum. */
if (emit_md5)
{
int b;
for (b = 0; b < NUM_MD5_BYTES; b++)
out_byte (files[i].md5[b]);
}
}
if (DWARF2_LINE_VERSION < 5)
/* Terminate filename list. */ /* Terminate filename list. */
out_byte (0); out_byte (0);
} }
@@ -1833,6 +2209,11 @@ out_debug_line (segT line_seg)
/* Version. */ /* Version. */
out_two (DWARF2_LINE_VERSION); out_two (DWARF2_LINE_VERSION);
if (DWARF2_LINE_VERSION >= 5)
{
out_byte (sizeof_address);
out_byte (0); /* Segment Selector size. */
}
/* Length of the prologue following this length. */ /* Length of the prologue following this length. */
prologue_start = symbol_temp_make (); prologue_start = symbol_temp_make ();
prologue_end = symbol_temp_make (); prologue_end = symbol_temp_make ();
@@ -1845,6 +2226,8 @@ out_debug_line (segT line_seg)
/* Parameters of the state machine. */ /* Parameters of the state machine. */
out_byte (DWARF2_LINE_MIN_INSN_LENGTH); out_byte (DWARF2_LINE_MIN_INSN_LENGTH);
if (DWARF2_LINE_VERSION >= 4)
out_byte (DWARF2_LINE_MAX_OPS_PER_INSN);
out_byte (DWARF2_LINE_DEFAULT_IS_STMT); out_byte (DWARF2_LINE_DEFAULT_IS_STMT);
out_byte (DWARF2_LINE_BASE); out_byte (DWARF2_LINE_BASE);
out_byte (DWARF2_LINE_RANGE); out_byte (DWARF2_LINE_RANGE);
@@ -1863,8 +2246,11 @@ out_debug_line (segT line_seg)
out_byte (0); /* DW_LNS_set_prologue_end */ out_byte (0); /* DW_LNS_set_prologue_end */
out_byte (0); /* DW_LNS_set_epilogue_begin */ out_byte (0); /* DW_LNS_set_epilogue_begin */
out_byte (1); /* DW_LNS_set_isa */ 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);
out_file_list (); out_dir_and_file_list ();
symbol_set_value_now (prologue_end); symbol_set_value_now (prologue_end);
@@ -2134,19 +2520,20 @@ out_debug_str (segT str_seg, symbolS **name_sym, symbolS **comp_dir_sym,
const char *dirname; const char *dirname;
char *p; char *p;
int len; int len;
int first_file = DWARF2_LINE_VERSION > 4 ? 0 : 1;
subseg_set (str_seg, 0); subseg_set (str_seg, 0);
/* DW_AT_name. We don't have the actual file name that was present /* DW_AT_name. We don't have the actual file name that was present
on the command line, so assume files[1] is the main input file. on the command line, so assume files[first_file] is the main input file.
We're not supposed to get called unless at least one line number We're not supposed to get called unless at least one line number
entry was emitted, so this should always be defined. */ entry was emitted, so this should always be defined. */
*name_sym = symbol_temp_new_now_octets (); *name_sym = symbol_temp_new_now_octets ();
if (files_in_use == 0) if (files_in_use == 0)
abort (); abort ();
if (files[1].dir) if (files[first_file].dir)
{ {
dirname = remap_debug_filename (dirs[files[1].dir]); dirname = remap_debug_filename (dirs[files[first_file].dir]);
len = strlen (dirname); len = strlen (dirname);
#ifdef TE_VMS #ifdef TE_VMS
/* Already has trailing slash. */ /* Already has trailing slash. */
@@ -2158,9 +2545,9 @@ out_debug_str (segT str_seg, symbolS **name_sym, symbolS **comp_dir_sym,
INSERT_DIR_SEPARATOR (p, len); INSERT_DIR_SEPARATOR (p, len);
#endif #endif
} }
len = strlen (files[1].filename) + 1; len = strlen (files[first_file].filename) + 1;
p = frag_more (len); p = frag_more (len);
memcpy (p, files[1].filename, len); memcpy (p, files[first_file].filename, len);
/* DW_AT_comp_dir */ /* DW_AT_comp_dir */
*comp_dir_sym = symbol_temp_new_now_octets (); *comp_dir_sym = symbol_temp_new_now_octets ();

View File

@@ -2,5 +2,18 @@
#name: DWARF5 .line 0 #name: DWARF5 .line 0
#readelf: -wl #readelf: -wl
#...
The Directory Table \(offset 0x.*, lines 3, columns 1\):
Entry Name
0 master directory
1 secondary directory
2 /tmp
The File Name Table \(offset 0x.*, lines 3, columns 3\):
Entry Dir MD5 Name
0 0 0x00000000000000000000000000000000 master source file
1 1 0x00000000000000000000000000000000 secondary source file
2 2 0x95828e8bc4f7404dbf7526fb7bd0f192 foo.c
#pass #pass

View File

@@ -1,17 +1,23 @@
.text .section .debug_info,"",%progbits
.4byte 0x8a
.2byte 0x2
.4byte .Ldebug_abbrev0
.byte 0x4
.uleb128 0x1
.file 0 .file 0 "master directory/master source file"
.line 1 .line 1
.long 3 .text
.word 0
.file 1 "asdf" .file 1 "secondary directory/secondary source file"
.line 2 .line 2
.long 5 .word 2
.file 0 .file "master source file"
.line 4 .line 4
.long 3 .word 4
.file 2 "def" .file 2 "/tmp" "foo.c" md5 0x95828e8bc4f7404dbf7526fb7bd0f192
.line 5 .line 5
.long 3 .word 6

View File

@@ -1,7 +1,7 @@
.*: Assembler messages: .*: Assembler messages:
.*:2: Error: file number less than one .*:2: Error: file number less than one
.*:3: Error: missing string .*:3: Error: missing string
.*:4: Error: file number 1 already allocated .*:4: Error: file table slot 1 is already occupied.*
.*:8: Error: unassigned file number 3 .*:8: Error: unassigned file number 3
.*:9: Error: junk at end of line, first unrecognized character is `1' .*:9: Error: junk at end of line, first unrecognized character is `1'
.*:12: Error: junk at end of line, first unrecognized character is `0' .*:12: Error: junk at end of line, first unrecognized character is `0'