From: Nick Clifton Date: Wed, 11 Mar 2020 10:17:14 +0000 (+0000) Subject: Add support for generating DWARF-5 format directory and file name tables from the... X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=5496f3c635dc;p=binutils-gdb.git 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. --- diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 0ca05640d82..9f54fa1f1c3 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,20 @@ +2020-03-11 Nick Clifton + + 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 PR 25651 diff --git a/binutils/dwarf.c b/binutils/dwarf.c index 2403ac7174b..c75059bd93a 100644 --- a/binutils/dwarf.c +++ b/binutils/dwarf.c @@ -3622,7 +3622,6 @@ read_debug_line_header (struct dwarf_section * section, { unsigned char *hdrptr; unsigned int initial_length_size; - unsigned char address_size, segment_selector_size; /* Extract information from the Line Number Program Header. (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) { - 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); - if (segment_selector_size != 0) + SAFE_BYTE_GET_AND_INC (linfo->li_segment_size, hdrptr, 1, end); + if (linfo->li_segment_size != 0) { warn (_("The %s section contains " "unsupported segment selector size: %d.\n"), - section->name, segment_selector_size); - return 0; + section->name, linfo->li_segment_size); + return NULL; } } @@ -3737,7 +3736,8 @@ display_formatted_table (unsigned char * data, unsigned char *format_start, format_count, *format, formati; dwarf_vma data_count, datai; 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); format_start = data; for (formati = 0; formati < format_count; formati++) @@ -3746,10 +3746,7 @@ display_formatted_table (unsigned char * data, SKIP_ULEB (data, end); if (data == end) { - if (is_dir) - warn (_("Corrupt directory format table entry\n")); - else - warn (_("Corrupt file name format table entry\n")); + warn (_("%s: Corrupt format description entry\n"), table_name); return data; } } @@ -3757,28 +3754,25 @@ display_formatted_table (unsigned char * data, READ_ULEB (data_count, data, end); if (data == end) { - if (is_dir) - warn (_("Corrupt directory list\n")); - else - warn (_("Corrupt file name list\n")); + warn (_("%s: Corrupt entry count\n"), table_name); return data; } if (data_count == 0) { - if (is_dir) - printf (_("\n The Directory Table is empty.\n")); - else - printf (_("\n The File Name Table is empty.\n")); + printf (_("\n The %s is empty.\n"), table_name); 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 Directory Table (offset 0x%lx):\n"), - (long) (data - start)); - else - printf (_("\n The File Name Table (offset 0x%lx):\n"), - (long) (data - start)); + printf (_("\n The %s (offset 0x%lx, lines %s, columns %u):\n"), + table_name, (long) (data - start), dwarf_vmatoa ("u", data_count), + format_count); printf (_(" Entry")); /* Delay displaying name as the last entry for better screen layout. */ @@ -3806,7 +3800,7 @@ display_formatted_table (unsigned char * data, printf (_("\tSize")); break; case DW_LNCT_MD5: - printf (_("\tMD5")); + printf (_("\tMD5\t\t\t")); break; default: printf (_("\t(Unknown format content type %s)"), @@ -3840,12 +3834,10 @@ display_formatted_table (unsigned char * data, section, NULL, '\t', -1); } } - if (data == end) + + if (data == end && (datai < data_count - 1)) { - if (is_dir) - warn (_("Corrupt directory entries list\n")); - else - warn (_("Corrupt file name entries list\n")); + warn (_("\n%s: Corrupt entries list\n"), table_name); return data; } putchar ('\n'); @@ -3909,6 +3901,11 @@ display_debug_lines_raw (struct dwarf_section * section, printf (_(" Offset: 0x%lx\n"), (long)(data - start)); printf (_(" Length: %ld\n"), (long) linfo.li_length); 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 (_(" Minimum Instruction Length: %d\n"), linfo.li_min_insn_length); if (linfo.li_version >= 4) diff --git a/binutils/dwarf.h b/binutils/dwarf.h index 69566926769..2249750f87a 100644 --- a/binutils/dwarf.h +++ b/binutils/dwarf.h @@ -29,6 +29,8 @@ typedef struct { dwarf_vma li_length; unsigned short li_version; + unsigned char li_address_size; + unsigned char li_segment_size; dwarf_vma li_prologue_length; unsigned char li_min_insn_length; unsigned char li_max_ops_per_insn; diff --git a/binutils/testsuite/binutils-all/dw5.W b/binutils/testsuite/binutils-all/dw5.W index 74c02566eec..2eccb03c5a6 100644 --- a/binutils/testsuite/binutils-all/dw5.W +++ b/binutils/testsuite/binutils-all/dw5.W @@ -291,6 +291,8 @@ Raw dump of debug contents of section .debug_line: Offset: 0x0 Length: 144 DWARF Version: 5 + Address size \(bytes\): 8 + Segment selector \(bytes\): 0 Prologue Length: 60 Minimum Instruction Length: 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 12 has 1 arg - The Directory Table \(offset 0x22\): + The Directory Table \(offset 0x22, lines 3, columns 1\): Entry Name 0 \(indirect line string, offset: 0x0\): 1 \(indirect line string, offset: 0x1\): 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 0 0 \(indirect line string, offset: 0x14\): main.c 1 1 \(indirect line string, offset: 0x1b\): main.c diff --git a/gas/ChangeLog b/gas/ChangeLog index 836cb5c6d9e..b4b1a341117 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,35 @@ +2020-03-11 Nick Clifton + + 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 * config/tc-csky.c (get_operand_value): Rewrite 1 << 31 expressions diff --git a/gas/NEWS b/gas/NEWS index 9a3f352108e..5623db64357 100644 --- a/gas/NEWS +++ b/gas/NEWS @@ -1,5 +1,9 @@ -*- 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: * Add -malign-branch-boundary=NUM, -malign-branch=TYPE[+TYPE...], diff --git a/gas/doc/as.texi b/gas/doc/as.texi index 711578fc762..0a6727ef848 100644 --- a/gas/doc/as.texi +++ b/gas/doc/as.texi @@ -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 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 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 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 @section @code{.fill @var{repeat} , @var{size} , @var{value}} diff --git a/gas/dwarf2dbg.c b/gas/dwarf2dbg.c index 181401300e6..d9d517a2998 100644 --- a/gas/dwarf2dbg.c +++ b/gas/dwarf2dbg.c @@ -30,6 +30,7 @@ #include "as.h" #include "safe-ctype.h" +#include "bignum.h" #ifdef HAVE_LIMITS_H #include @@ -81,11 +82,11 @@ #endif #ifndef DWARF2_FILE_TIME_NAME -#define DWARF2_FILE_TIME_NAME(FILENAME,DIRNAME) 0 +#define DWARF2_FILE_TIME_NAME(FILENAME,DIRNAME) -1 #endif #ifndef DWARF2_FILE_SIZE_NAME -#define DWARF2_FILE_SIZE_NAME(FILENAME,DIRNAME) 0 +#define DWARF2_FILE_SIZE_NAME(FILENAME,DIRNAME) -1 #endif #ifndef DWARF2_VERSION @@ -99,7 +100,7 @@ /* This implementation outputs version 3 .debug_line information. */ #ifndef DWARF2_LINE_VERSION -#define DWARF2_LINE_VERSION 3 +#define DWARF2_LINE_VERSION (dwarf_level > 3 ? dwarf_level : 3) #endif #include "subsegs.h" @@ -147,6 +148,10 @@ /* Flag that indicates the initial value of the is_stmt_start flag. */ #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. */ #define SPECIAL_LINE(op) \ (((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 **last_seg_ptr; +#define NUM_MD5_BYTES 16 + struct file_entry { - const char *filename; - unsigned int dir; + const char * filename; + unsigned int dir; + unsigned char md5[NUM_MD5_BYTES]; }; /* Table of files used by .debug_line. */ @@ -208,9 +216,9 @@ static unsigned int files_in_use; static unsigned int files_allocated; /* Table of directories used by .debug_line. */ -static char **dirs; -static unsigned int dirs_in_use; -static unsigned int dirs_allocated; +static char ** dirs = NULL; +static unsigned int dirs_in_use = 0; +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. */ @@ -239,8 +247,6 @@ static symbolS *view_assert_failed; /* The size of an address on the target. */ static unsigned int sizeof_address; -static unsigned int get_filenum (const char *, unsigned int); - #ifndef TC_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; seg_info (seg)->dwarf2_line_seg = s; } + gas_assert (seg == s->seg); 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; /* 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; /* 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); } +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 been encountered, the info for the corresponding 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)); 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->flags = DWARF2_FLAG_IS_STMT; line->isa = current.isa; @@ -670,108 +997,6 @@ dwarf2_emit_label (symbolS *label) 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: - Pass .file "source.c" to s_app_file - 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 * dwarf2_directive_filename (void) { + bfd_boolean with_md5 = TRUE; valueT num; char *filename; + const char * dirname = NULL; int filename_len; /* Continue to accept a bare string and pass it off. */ @@ -795,24 +1022,48 @@ dwarf2_directive_filename (void) 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")); ignore_rest_of_line (); return NULL; } - if (num == 0) - { - demand_empty_rest_of_line (); - return NULL; - } + /* FIXME: Should we allow ".file \n" as an expression meaning + "switch back to the already allocated file as the current + file" ? */ filename = demand_copy_C_string (&filename_len); if (filename == NULL) /* demand_copy_C_string will have already generated an error message. */ return NULL; + /* For DWARF-5 support we also accept: + .file [""] "" [md5 ] */ + 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 (); /* 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); 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; } @@ -861,10 +1109,14 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) if (filenum < 1) { - as_bad (_("file number less than one")); - return; + if (filenum != 0 || DWARF2_LINE_VERSION < 5) + { + as_bad (_("file number less than one")); + 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); return; @@ -1710,14 +1962,40 @@ process_entries (segT seg, struct line_entry *e) /* Emit the directory and file tables for .debug_line. */ static void -out_file_list (void) +out_dir_and_file_list (void) { 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; + /* 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. */ + 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) { dir = remap_debug_filename (dirs[i]); @@ -1725,19 +2003,89 @@ out_file_list (void) cp = frag_more (size); memcpy (cp, dir, size); } - /* Terminate it. */ - out_byte ('\0'); - for (i = 1; i < files_in_use; ++i) + if (DWARF2_LINE_VERSION < 5) + /* Terminate it. */ + out_byte ('\0'); + + /* 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; if (files[i].filename == NULL) { - as_bad (_("unassigned file number %ld"), (long) i); /* Prevent a crash later, particularly for file 1. */ files[i].filename = ""; - continue; + if (DWARF2_LINE_VERSION < 5 || i != 0) + { + as_bad (_("unassigned file number %ld"), (long) i); + continue; + } } fullfilename = DWARF2_FILE_NAME (files[i].filename, @@ -1746,17 +2094,45 @@ out_file_list (void) cp = frag_more (size); memcpy (cp, fullfilename, size); - out_uleb128 (files[i].dir); /* directory number */ + /* Directory number. */ + out_uleb128 (files[i].dir); + /* Output the last modification timestamp. */ - out_uleb128 (DWARF2_FILE_TIME_NAME (files[i].filename, - files[i].dir ? dirs [files [i].dir] : "")); + if (emit_timestamps) + { + offsetT timestamp; + + 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. */ - out_uleb128 (DWARF2_FILE_SIZE_NAME (files[i].filename, - files[i].dir ? dirs [files [i].dir] : "")); + 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]); + } } - /* Terminate filename list. */ - out_byte (0); + if (DWARF2_LINE_VERSION < 5) + /* Terminate filename list. */ + out_byte (0); } /* Switch to SEC and output a header length field. Return the size of @@ -1833,6 +2209,11 @@ out_debug_line (segT line_seg) /* 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. */ prologue_start = symbol_temp_make (); prologue_end = symbol_temp_make (); @@ -1845,6 +2226,8 @@ out_debug_line (segT line_seg) /* Parameters of the state machine. */ 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_BASE); 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_epilogue_begin */ 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); @@ -2134,19 +2520,20 @@ out_debug_str (segT str_seg, symbolS **name_sym, symbolS **comp_dir_sym, const char *dirname; char *p; int len; + int first_file = DWARF2_LINE_VERSION > 4 ? 0 : 1; subseg_set (str_seg, 0); /* 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 entry was emitted, so this should always be defined. */ *name_sym = symbol_temp_new_now_octets (); if (files_in_use == 0) 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); #ifdef TE_VMS /* 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); #endif } - len = strlen (files[1].filename) + 1; + len = strlen (files[first_file].filename) + 1; p = frag_more (len); - memcpy (p, files[1].filename, len); + memcpy (p, files[first_file].filename, len); /* DW_AT_comp_dir */ *comp_dir_sym = symbol_temp_new_now_octets (); diff --git a/gas/testsuite/gas/elf/dwarf-5-file0.d b/gas/testsuite/gas/elf/dwarf-5-file0.d index a3d1fdfb6e7..3dffa63965b 100644 --- a/gas/testsuite/gas/elf/dwarf-5-file0.d +++ b/gas/testsuite/gas/elf/dwarf-5-file0.d @@ -2,5 +2,18 @@ #name: DWARF5 .line 0 #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 + diff --git a/gas/testsuite/gas/elf/dwarf-5-file0.s b/gas/testsuite/gas/elf/dwarf-5-file0.s index 1f99c589239..2792ba8c441 100644 --- a/gas/testsuite/gas/elf/dwarf-5-file0.s +++ b/gas/testsuite/gas/elf/dwarf-5-file0.s @@ -1,17 +1,23 @@ - .text + .section .debug_info,"",%progbits + .4byte 0x8a + .2byte 0x2 + .4byte .Ldebug_abbrev0 + .byte 0x4 + .uleb128 0x1 - .file 0 - .line 1 - .long 3 + .file 0 "master directory/master source file" + .line 1 + .text + .word 0 - .file 1 "asdf" - .line 2 - .long 5 + .file 1 "secondary directory/secondary source file" + .line 2 + .word 2 - .file 0 - .line 4 - .long 3 + .file "master source file" + .line 4 + .word 4 - .file 2 "def" - .line 5 - .long 3 + .file 2 "/tmp" "foo.c" md5 0x95828e8bc4f7404dbf7526fb7bd0f192 + .line 5 + .word 6 diff --git a/gas/testsuite/gas/lns/lns-diag-1.l b/gas/testsuite/gas/lns/lns-diag-1.l index 53f993605de..1256e85cfcb 100644 --- a/gas/testsuite/gas/lns/lns-diag-1.l +++ b/gas/testsuite/gas/lns/lns-diag-1.l @@ -1,7 +1,7 @@ .*: Assembler messages: .*:2: Error: file number less than one .*: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 .*:9: Error: junk at end of line, first unrecognized character is `1' .*:12: Error: junk at end of line, first unrecognized character is `0'