From patchwork Tue Apr 9 04:57:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Will Hawkins X-Patchwork-Id: 88217 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id AA9E4385829B for ; Tue, 9 Apr 2024 04:58:12 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mail-qt1-x82c.google.com (mail-qt1-x82c.google.com [IPv6:2607:f8b0:4864:20::82c]) by sourceware.org (Postfix) with ESMTPS id 4E5DA3858CD1 for ; Tue, 9 Apr 2024 04:57:39 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 4E5DA3858CD1 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=obs.cr Authentication-Results: sourceware.org; spf=none smtp.mailfrom=obs.cr ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 4E5DA3858CD1 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::82c ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1712638664; cv=none; b=PE/HA7p2uVkHF3RaiUSFhaLi6foofjL9AdwG9ueEuiOtbaQwvwJLmyUBrjif1XEQ6vW0hn0cQSQRA23r18muFLHLfMdgTTFE7cIfdtqbFej2UOgCLFILa6BsUfoHnRNn08Z6nIcQl88iCQkJ91QJJOL/ao6TED0p5G1mMgf/u90= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1712638664; c=relaxed/simple; bh=L3tgMJhHi/sX6ow+nsCTY/byYGnyLwAmgkqj8D4yYOQ=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=UtRNT+OB6hrHU4gPhr5ZEYNsCyBsQOLxc/GNuWVaCpQYRHmacheskUe27AbyEvTf1HUEr/0opVDxc2lsMHHoxa8GVV9uII02vFKeNPna81IL+wq91vteGoAEGeKwcJh3EX986r7G4ZuzJXdOYE+tOi6hBeZp6sFWk5m1/g/zN54= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-qt1-x82c.google.com with SMTP id d75a77b69052e-43492c1a8a4so8504241cf.1 for ; Mon, 08 Apr 2024 21:57:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=obs-cr.20230601.gappssmtp.com; s=20230601; t=1712638658; x=1713243458; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=QPFZfhuKdErk+pzvCirCeG+fYYaUjKyxSXPVqh88dK0=; b=vWpnxI8BaHPxt/I514wjABMYv+jVS8TnzK1VDoPGZPYKD7fzNDLQ5uD07nvD83UBtK wAGsg2XEkI3T+euReWwcBQyCbD2ZCXiiuVreHxOMIdWNMzlvzn+gRA6dD7LWD0BEzfXn E2zeMbzfUjGxBtMIJ0+Yuwlkho6mgEkJqzjZaPQ/tBTt2uQInqcAO7P96qAM4xKYzYLS o77g6hhAJbkZ43B2RFJ+4Wer5ca3xt0z3W3yVRWbV0e1JGyj3+OVqecCIJDBZdM7gyRD ZSyVBw896Swv6tMhJ0dNwm06mgp4FN7StqTs6E3/eEHKhIttTq5rwFjkbxSTqP9yNcC/ v5lQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712638658; x=1713243458; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=QPFZfhuKdErk+pzvCirCeG+fYYaUjKyxSXPVqh88dK0=; b=itsRxTEpQEmY5LI3wX5T6VNK7cswXRqJetrE7W+DOGV9dMXCF4s104NWnis8EWJ99M QZOeMyd7Ab5akVpGwwIV0O0j1QDxXEjIJJXzNGoYyHBKe2vxQsp+e4NWbYQP5eg02+os hYJsMQPBySIxdYkCz2HVYKyVFDziL1l9UDRN1pWoa9bI8OKpthH4s7KpqHzWN25mCgwM TVtbeR2vDsVOvX6WynbMg6/unEOIAfmIoXSKyOr5DdstIBXWsUbdvhlobDCXiZRgQFK+ 1vWVjJW+Xniq5jVa1STJ2vwmly6A5XRwyDJGMebO9GdOKHuqR284RpabQgrD73uUluXX 7EFw== X-Gm-Message-State: AOJu0YwxgUzxwG26fOQic9/gm79K07GyC911QFn0g95bRZ/ckgKHH8gv KySrX7eSnpfWuDWAUi+mOxuf588id6zgxZOGfoAxnHzvw8x0bNu/ghLT6edy9UxismiEMToDtvp G X-Google-Smtp-Source: AGHT+IGdQeke9J82cqFNc/2R4RJX6LKgOeD0HBOHwHYt+ND6TNlMEhsl5ZyQ89Ulf8ryZfLQPsi4lw== X-Received: by 2002:ac8:580e:0:b0:432:a566:cf72 with SMTP id g14-20020ac8580e000000b00432a566cf72mr14364599qtg.23.1712638657687; Mon, 08 Apr 2024 21:57:37 -0700 (PDT) Received: from ininer.rhod.uc.edu ([129.137.96.15]) by smtp.gmail.com with ESMTPSA id kg18-20020a05622a761200b00434c9e59ab4sm657742qtb.25.2024.04.08.21.57.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Apr 2024 21:57:37 -0700 (PDT) From: Will Hawkins To: gdb-patches@sourceware.org Cc: Will Hawkins Subject: [PATCH v4] gdb: Support embedded source in DWARF Date: Tue, 9 Apr 2024 00:57:33 -0400 Message-ID: <20240409045735.32562-1-hawkinsw@obs.cr> X-Mailer: git-send-email 2.44.0 MIME-Version: 1.0 X-Spam-Status: No, score=-9.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org While DW_LNCT_source is not yet finalized in the DWARF standard (https://dwarfstd.org/issues/180201.1.html), LLVM does emit it. This patch adds support for it in gdb. Signed-off-by: Will Hawkins --- Notes: v3 -> v4 - Initialize the source pointer in the symtab to NULL. - Fix a bug that caused an error generating formatted output. v2 -> v3 - Address feedback from v2. - Fixed up a bug where non-CU embedded files were incorrectly marked as embedded. - Revamped source access through the cache to avoid copies. v1 -> v2 - Address feedback from original PR - Add support for maintenance commands to see embedded source status - Prevent access to the filesystem for symtabs with embedded source - Add additional unit tests gdb/dwarf2/file-and-dir.h | 23 ++++- gdb/dwarf2/line-header.c | 22 +++-- gdb/dwarf2/line-header.h | 9 +- gdb/dwarf2/read.c | 83 +++++++++++++++--- gdb/extension.c | 7 +- gdb/extension.h | 9 +- gdb/source-cache.c | 87 ++++++++++++------- gdb/source-cache.h | 12 +-- gdb/source.c | 74 +++++++++++----- gdb/source.h | 4 + gdb/symfile.c | 1 + gdb/symmisc.c | 17 ++-- gdb/symtab.h | 5 ++ gdb/testsuite/gdb.dwarf2/dw2-lnct-source.c | 24 ++++++ gdb/testsuite/gdb.dwarf2/dw2-lnct-source.exp | 88 ++++++++++++++++++++ gdb/testsuite/lib/dwarf.exp | 19 +++-- include/dwarf2.h | 5 ++ 17 files changed, 391 insertions(+), 98 deletions(-) create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-lnct-source.c create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-lnct-source.exp diff --git a/gdb/dwarf2/file-and-dir.h b/gdb/dwarf2/file-and-dir.h index a5b1d8a3a21..84a7367e973 100644 --- a/gdb/dwarf2/file-and-dir.h +++ b/gdb/dwarf2/file-and-dir.h @@ -95,7 +95,14 @@ struct file_and_directory const char *get_fullname () { if (m_fullname == nullptr) - m_fullname = find_source_or_rewrite (get_name (), get_comp_dir ()); + { + if (m_is_embedded) + m_fullname = make_unique_xstrdup (embedded_fullname ( + get_name (), + get_comp_dir ())); + else + m_fullname = find_source_or_rewrite (get_name (), get_comp_dir ()); + } return m_fullname.get (); } @@ -105,6 +112,17 @@ struct file_and_directory m_fullname.reset (); } + /* Set whether the file's source is embedded in the dwarf. */ + void set_embedded (bool is_embedded) + { + m_is_embedded = is_embedded; + } + + /* Return true if the file's source is embedded in the dwarf. */ + bool is_embedded () const + { + return m_is_embedded; + } private: /* The filename. */ @@ -124,6 +142,9 @@ struct file_and_directory /* The full name. */ gdb::unique_xmalloc_ptr m_fullname; + + /* Whether the file's source is embedded in the dwarf. */ + bool m_is_embedded = false; }; #endif /* GDB_DWARF2_FILE_AND_DIR_H */ diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c index a3ca49b64f5..3bc707e999e 100644 --- a/gdb/dwarf2/line-header.c +++ b/gdb/dwarf2/line-header.c @@ -45,6 +45,7 @@ line_header::add_include_dir (const char *include_dir) void line_header::add_file_name (const char *name, dir_index d_index, + const char *source, unsigned int mod_time, unsigned int length) { @@ -54,7 +55,7 @@ line_header::add_file_name (const char *name, if (dwarf_line_debug >= 2) gdb_printf (gdb_stdlog, "Adding file %d: %s\n", index, name); - m_file_names.emplace_back (name, index, d_index, mod_time, length); + m_file_names.emplace_back (name, index, d_index, source, mod_time, length); } std::string @@ -125,6 +126,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd, void (*callback) (struct line_header *lh, const char *name, dir_index d_index, + const char *source, unsigned int mod_time, unsigned int length)) { @@ -239,13 +241,17 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd, break; case DW_LNCT_MD5: break; + case DW_LNCT_LLVM_SOURCE: + if (string.has_value () && (*string)[0] != '\0') + fe.source = *string; + break; default: complaint (_("Unknown format content type %s"), pulongest (content_type)); } } - callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length); + callback (lh, fe.name, fe.d_index, fe.source, fe.mod_time, fe.length); } *bufp = buf; @@ -368,8 +374,8 @@ dwarf_decode_line_header (sect_offset sect_off, bool is_dwz, read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (), offset_size, [] (struct line_header *header, const char *name, - dir_index d_index, unsigned int mod_time, - unsigned int length) + dir_index d_index, const char *source, + unsigned int mod_time, unsigned int length) { header->add_include_dir (name); }); @@ -378,10 +384,10 @@ dwarf_decode_line_header (sect_offset sect_off, bool is_dwz, read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (), offset_size, [] (struct line_header *header, const char *name, - dir_index d_index, unsigned int mod_time, - unsigned int length) + dir_index d_index, const char *source, + unsigned int mod_time, unsigned int length) { - header->add_file_name (name, d_index, mod_time, length); + header->add_file_name (name, d_index, source, mod_time, length); }); } else @@ -408,7 +414,7 @@ dwarf_decode_line_header (sect_offset sect_off, bool is_dwz, length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read); line_ptr += bytes_read; - lh->add_file_name (cur_file, d_index, mod_time, length); + lh->add_file_name (cur_file, d_index, nullptr, mod_time, length); } line_ptr += bytes_read; } diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h index c068dff70a3..abc95f3ee87 100644 --- a/gdb/dwarf2/line-header.h +++ b/gdb/dwarf2/line-header.h @@ -35,9 +35,10 @@ struct file_entry file_entry () = default; file_entry (const char *name_, file_name_index index_, dir_index d_index_, - unsigned int mod_time_, unsigned int length_) + const char *source_, unsigned int mod_time_, unsigned int length_) : name (name_), index (index_), + source (source_), d_index (d_index_), mod_time (mod_time_), length (length_) @@ -54,6 +55,10 @@ struct file_entry /* The index of this file in the file table. */ file_name_index index {}; + /* The file's contents (if not null). Note this is an observing pointer. + The memory is owned by debug_line_buffer. */ + const char *source {}; + /* The directory index (1-based). */ dir_index d_index {}; @@ -88,7 +93,7 @@ struct line_header void add_include_dir (const char *include_dir); /* Add an entry to the file name table. */ - void add_file_name (const char *name, dir_index d_index, + void add_file_name (const char *name, dir_index d_index, const char *source, unsigned int mod_time, unsigned int length); /* Return the include dir at INDEX (0-based in DWARF 5 and 1-based before). diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 7442094874c..e35ff019acf 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -1635,6 +1635,10 @@ struct quick_file_names /* The file names from the line table after being run through gdb_realpath. These are computed lazily. */ const char **real_names; + + /* Whether or not the file names refer to sources embedded + in the dwarf. */ + const bool *embeddeds; }; /* With OBJF_READNOW, the DWARF reader expands all CUs immediately. @@ -1908,27 +1912,39 @@ dw2_get_file_names_reader (const struct die_reader_specs *reader, if (slot != nullptr) *slot = qfn; + + bool cu_file_embedded = false; + std::vector embeddedv; + std::vector include_names; if (lh != nullptr) { for (const auto &entry : lh->file_names ()) { std::string name_holder; - const char *include_name = - compute_include_file_name (lh.get (), entry, fnd, name_holder); + const char *include_name + = compute_include_file_name (lh.get (), entry, fnd, name_holder); if (include_name != nullptr) { include_name = per_objfile->objfile->intern (include_name); include_names.push_back (include_name); + embeddedv.push_back (entry.source != nullptr); } + else if (entry.source != nullptr) + { + /* We have an embedded source for the CU. */ + gdb_assert (offset == 1); + cu_file_embedded = true; + } + } } qfn->num_file_names = offset + include_names.size (); qfn->comp_dir = fnd.intern_comp_dir (per_objfile->objfile); - qfn->file_names = - XOBNEWVEC (&per_objfile->per_bfd->obstack, const char *, - qfn->num_file_names); + qfn->file_names + = XOBNEWVEC (&per_objfile->per_bfd->obstack, const char *, + qfn->num_file_names); if (offset != 0) qfn->file_names[0] = per_objfile->objfile->intern (fnd.get_name ()); @@ -1936,7 +1952,16 @@ dw2_get_file_names_reader (const struct die_reader_specs *reader, memcpy (&qfn->file_names[offset], include_names.data (), include_names.size () * sizeof (const char *)); - qfn->real_names = NULL; + bool *embeddeds + = XOBNEWVEC (&per_objfile->per_bfd->obstack, bool, + qfn->num_file_names); + if (cu_file_embedded) + embeddeds[0] = true; + for (size_t i = 0; i < embeddedv.size (); i++) + embeddeds[offset + i] = embeddedv[i]; + qfn->embeddeds = embeddeds; + + qfn->real_names = nullptr; lh_cu->file_names = qfn; } @@ -1980,7 +2005,11 @@ dw2_get_real_path (dwarf2_per_objfile *per_objfile, dirname = qfn->comp_dir; gdb::unique_xmalloc_ptr fullname; - fullname = find_source_or_rewrite (qfn->file_names[index], dirname); + + if (qfn->embeddeds[index]) + fullname.reset (embedded_fullname (dirname, qfn->file_names[index])); + else + fullname = find_source_or_rewrite (qfn->file_names[index], dirname); qfn->real_names[index] = fullname.release (); } @@ -7311,6 +7340,35 @@ find_file_and_directory (struct die_info *die, struct dwarf2_cu *cu) file_and_directory res (dwarf2_string_attr (die, DW_AT_name, cu), dwarf2_string_attr (die, DW_AT_comp_dir, cu)); + /* Because the line header may tell us information about the CU + filename (e.g., whether it is embedded) which will affect other + calculations, we have to read that information here. */ + line_header *lh = cu->line_header; + struct attribute *attr = dwarf2_attr (die, DW_AT_stmt_list, cu); + if (lh == nullptr && attr != nullptr && attr->form_is_unsigned ()) + { + sect_offset line_offset = (sect_offset) attr->as_unsigned (); + line_header_up lhu = dwarf_decode_line_header (line_offset, cu, + res.get_comp_dir ()); + if (lhu != nullptr) + lh = lhu.release(); + } + + if (lh != nullptr) + { + for (const auto &entry : lh->file_names ()) + { + if (entry.source == nullptr) + continue; + + std::string name_holder; + const char *include_name = + compute_include_file_name (lh, entry, res, name_holder); + if (include_name == nullptr) + res.set_embedded (true); + } + } + if (res.get_comp_dir () == nullptr && producer_is_gcc_lt_4_3 (cu) && res.get_name () != nullptr @@ -18448,7 +18506,7 @@ dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu, length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read); line_ptr += bytes_read; - lh->add_file_name (cur_file, dindex, mod_time, length); + lh->add_file_name (cur_file, dindex, nullptr, mod_time, length); } break; case DW_LNE_set_discriminator: @@ -18603,9 +18661,12 @@ dwarf_decode_lines (struct line_header *lh, struct dwarf2_cu *cu, subfile *sf = builder->get_current_subfile (); if (sf->symtab == nullptr) - sf->symtab = allocate_symtab (cust, sf->name.c_str (), - sf->name_for_id.c_str ()); - + { + sf->symtab = allocate_symtab (cust, sf->name.c_str (), + sf->name_for_id.c_str ()); + if (fe.source) + sf->symtab->source = fe.source; + } fe.symtab = sf->symtab; } } diff --git a/gdb/extension.c b/gdb/extension.c index 9db8b53a087..f17991a3751 100644 --- a/gdb/extension.c +++ b/gdb/extension.c @@ -991,16 +991,19 @@ xmethod_worker::get_result_type (value *object, gdb::array_view args) /* See extension.h. */ std::optional -ext_lang_colorize (const std::string &filename, const std::string &contents) +ext_lang_colorize (const std::string &filename, const std::string_view contents) { std::optional result; + /* We avoided copies as long as possible. The external colorization API + requires a std::string. */ + std::string contents_storage = std::string (contents); for (const struct extension_language_defn *extlang : extension_languages) { if (extlang->ops == nullptr || extlang->ops->colorize == nullptr) continue; - result = extlang->ops->colorize (filename, contents); + result = extlang->ops->colorize (filename, contents_storage); if (result.has_value ()) return result; } diff --git a/gdb/extension.h b/gdb/extension.h index 5260bcbde00..e5a5333b7c3 100644 --- a/gdb/extension.h +++ b/gdb/extension.h @@ -319,12 +319,13 @@ extern void get_matching_xmethod_workers std::vector *workers); /* Try to colorize some source code. FILENAME is the name of the file - holding the code. CONTENTS is the source code itself. This will - either a colorized (using ANSI terminal escapes) version of the - source code, or an empty value if colorizing could not be done. */ + holding the code. CONTENTS is a view of the source code itself. + This will either generate a colorized (using ANSI terminal escapes) + version of the source code, or an empty value if colorizing could not + be done. */ extern std::optional ext_lang_colorize - (const std::string &filename, const std::string &contents); + (const std::string &filename, const std::string_view contents); /* Try to colorize a single line of disassembler output, CONTENT for GDBARCH. This will return either a colorized (using ANSI terminal diff --git a/gdb/source-cache.c b/gdb/source-cache.c index 8b5bd84d19a..f5168921f99 100644 --- a/gdb/source-cache.c +++ b/gdb/source-cache.c @@ -93,32 +93,45 @@ set_use_gnu_source_highlight_enabled (const char *ignore_args, /* See source-cache.h. */ -std::string +std::string_view source_cache::get_plain_source_lines (struct symtab *s, - const std::string &fullname) + const std::string &fullname, + std::string &source_storage) { - scoped_fd desc (open_source_file (s)); - if (desc.get () < 0) - perror_with_name (symtab_to_filename_for_display (s), -desc.get ()); + std::string_view lines; + if (!s->source) + { + + scoped_fd desc (open_source_file (s)); + if (desc.get () < 0) + perror_with_name (symtab_to_filename_for_display (s), -desc.get ()); - struct stat st; - if (fstat (desc.get (), &st) < 0) - perror_with_name (symtab_to_filename_for_display (s)); + struct stat st; + if (fstat (desc.get (), &st) < 0) + perror_with_name (symtab_to_filename_for_display (s)); - std::string lines; - lines.resize (st.st_size); - if (myread (desc.get (), &lines[0], lines.size ()) < 0) - perror_with_name (symtab_to_filename_for_display (s)); + source_storage.resize (st.st_size); + if (myread (desc.get (), + &source_storage[0], + source_storage.size ()) < 0) + perror_with_name (symtab_to_filename_for_display (s)); - time_t mtime = 0; - if (s->compunit ()->objfile () != NULL - && s->compunit ()->objfile ()->obfd != NULL) - mtime = s->compunit ()->objfile ()->mtime; - else if (current_program_space->exec_bfd ()) - mtime = current_program_space->ebfd_mtime; + time_t mtime = 0; + if (s->compunit ()->objfile () != nullptr + && s->compunit ()->objfile ()->obfd != nullptr) + mtime = s->compunit ()->objfile ()->mtime; + else if (current_program_space->exec_bfd ()) + mtime = current_program_space->ebfd_mtime; - if (mtime && mtime < st.st_mtime) - warning (_("Source file is more recent than executable.")); + if (mtime && mtime < st.st_mtime) + warning (_("Source file is more recent than executable.")); + + lines = source_storage; + } + else + { + lines = s->source; + } std::vector offsets; offsets.push_back (0); @@ -200,9 +213,10 @@ get_language_name (enum language lang) succeeded. */ static bool -try_source_highlight (std::string &contents ATTRIBUTE_UNUSED, +try_source_highlight (std::string_view &contents ATTRIBUTE_UNUSED, enum language lang ATTRIBUTE_UNUSED, - const std::string &fullname ATTRIBUTE_UNUSED) + const std::string &fullname ATTRIBUTE_UNUSED, + std::string &contents_storage) { #ifdef HAVE_SOURCE_HIGHLIGHT if (!use_gnu_source_highlight) @@ -240,10 +254,14 @@ try_source_highlight (std::string &contents ATTRIBUTE_UNUSED, lang_name = detected_lang.c_str (); } - std::istringstream input (contents); + /* We waited as long as possible but now we need a string. */ + contents_storage = contents; + std::istringstream input (contents_storage); std::ostringstream output; highlighter->highlight (input, output, lang_name, fullname); - contents = std::move (output).str (); + /* Use the given storage for the contents and set the + view appropriately. */ + contents = contents_storage = std::move (output).str (); styled = true; } catch (...) @@ -275,13 +293,16 @@ static void gnu_source_highlight_test () "}\n"); const std::string fullname = "test.c"; std::string styled_prog; + std::string_view styled_prog_view; bool res = false; bool saw_exception = false; styled_prog = prog; + styled_prog_view = styled_prog; try { - res = try_source_highlight (styled_prog, language_c, fullname); + res = try_source_highlight (styled_prog_view, language_c, fullname, + styled_prog); } catch (...) { @@ -324,10 +345,12 @@ source_cache::ensure (struct symtab *s) } } - std::string contents; + std::string_view source_contents; + std::string source_contents_storage; try { - contents = get_plain_source_lines (s, fullname); + source_contents = get_plain_source_lines (s, fullname, + source_contents_storage); } catch (const gdb_exception_error &e) { @@ -339,15 +362,17 @@ source_cache::ensure (struct symtab *s) && m_no_styling_files.count (fullname) == 0) { bool already_styled - = try_source_highlight (contents, s->language (), fullname); + = try_source_highlight (source_contents, s->language (), fullname, + source_contents_storage); if (!already_styled) { std::optional ext_contents; - ext_contents = ext_lang_colorize (fullname, contents); + ext_contents = ext_lang_colorize (fullname, source_contents); if (ext_contents.has_value ()) { - contents = std::move (*ext_contents); + source_contents = source_contents_storage + = std::move (*ext_contents); already_styled = true; } } @@ -369,7 +394,7 @@ source_cache::ensure (struct symtab *s) } } - source_text result = { std::move (fullname), std::move (contents) }; + source_text result = { std::move (fullname), std::string (source_contents) }; m_source_map.push_back (std::move (result)); if (m_source_map.size () > MAX_ENTRIES) diff --git a/gdb/source-cache.h b/gdb/source-cache.h index d4cb7d00ae8..84996a6a5a1 100644 --- a/gdb/source-cache.h +++ b/gdb/source-cache.h @@ -80,11 +80,13 @@ class source_cache std::string contents; }; - /* A helper function for get_source_lines reads a source file. - Returns the contents of the file; or throws an exception on - error. This also updates m_offset_cache. */ - std::string get_plain_source_lines (struct symtab *s, - const std::string &fullname); + /* A helper function for get_source_lines that reads a source file. + Returns a view of the contents of the file using SOURCE_STORAGE + as necessary (i.e., if the source is stored on disk); or throws + an exception on error. This also updates m_offset_cache. */ + std::string_view get_plain_source_lines (struct symtab *s, + const std::string &fullname, + std::string &source_storage); /* A helper function that the data for the given symtab is entered into both caches. Returns false on error. */ diff --git a/gdb/source.c b/gdb/source.c index bbeb4154258..8bfaffe43a8 100644 --- a/gdb/source.c +++ b/gdb/source.c @@ -684,9 +684,11 @@ info_source_command (const char *ignore, int from_tty) cust = s->compunit (); gdb_printf (_("Current source file is %s\n"), s->filename); - if (s->compunit ()->dirname () != NULL) + if (s->compunit ()->dirname () != nullptr) gdb_printf (_("Compilation directory is %s\n"), s->compunit ()->dirname ()); - if (s->fullname) + if (s->source != nullptr) + gdb_printf (_("With embedded source.\n")); + else if (s->fullname) gdb_printf (_("Located in %s\n"), s->fullname); const std::vector *offsets; if (g_source_cache.get_line_charpos (s, &offsets)) @@ -960,6 +962,19 @@ source_full_path_of (const char *filename, return 1; } +/* See source.h. */ + +char * +embedded_fullname (const char *dirname, const char *filename) +{ + if (dirname != nullptr) + { + return concat (dirname, SLASH_STRING, filename, (char *) nullptr); + } + + return xstrdup (filename); +} + /* Return non-zero if RULE matches PATH, that is if the rule can be applied to PATH. */ @@ -1237,27 +1252,35 @@ symtab_to_fullname (struct symtab *s) /* Use cached copy if we have it. We rely on forget_cached_source_info being called appropriately to handle cases like the file being moved. */ - if (s->fullname == NULL) + if (s->fullname == nullptr) { - scoped_fd fd = open_source_file (s); - - if (fd.get () < 0) + if (s->source) + s->fullname = embedded_fullname (s->compunit ()->dirname (), + s->filename); + else { - gdb::unique_xmalloc_ptr fullname; + scoped_fd fd = open_source_file (s); - /* rewrite_source_path would be applied by find_and_open_source, we - should report the pathname where GDB tried to find the file. */ + if (fd.get () < 0) + { + gdb::unique_xmalloc_ptr fullname; - if (s->compunit ()->dirname () == nullptr - || IS_ABSOLUTE_PATH (s->filename)) - fullname.reset (xstrdup (s->filename)); - else - fullname.reset (concat (s->compunit ()->dirname (), SLASH_STRING, - s->filename, (char *) NULL)); + /* rewrite_source_path would be applied by find_and_open_source, + we should report the pathname where GDB tried to find the + file. */ - s->fullname = rewrite_source_path (fullname.get ()).release (); - if (s->fullname == NULL) - s->fullname = fullname.release (); + if (s->compunit ()->dirname () == nullptr + || IS_ABSOLUTE_PATH (s->filename)) + fullname.reset (xstrdup (s->filename)); + else + fullname.reset (concat (s->compunit ()->dirname (), + SLASH_STRING, s->filename, + (char *) nullptr)); + + s->fullname = rewrite_source_path (fullname.get ()).release (); + if (s->fullname == nullptr) + s->fullname = fullname.release (); + } } } @@ -1317,12 +1340,17 @@ print_source_lines_base (struct symtab *s, int line, int stopline, else { last_source_visited = s; - scoped_fd desc = open_source_file (s); - last_source_error = desc.get () < 0; - if (last_source_error) + /* Do not attempt to open a source file for a symtab + with an embedded source. */ + if (!s->source) { - noprint = true; - errcode = -desc.get (); + scoped_fd desc = open_source_file (s); + last_source_error = desc.get () < 0; + if (last_source_error) + { + noprint = true; + errcode = -desc.get (); + } } } } diff --git a/gdb/source.h b/gdb/source.h index 144ee48f722..f27caf1a9b5 100644 --- a/gdb/source.h +++ b/gdb/source.h @@ -216,4 +216,8 @@ extern void forget_cached_source_info (void); need to would make things slower than necessary. */ extern void select_source_symtab (); +/* Compute the fullname for a source file whose source is embedded + in the dwarf file. */ +extern char *embedded_fullname (const char *dirname, + const char *filename); #endif diff --git a/gdb/symfile.c b/gdb/symfile.c index db6d76e78bf..48b297b540b 100644 --- a/gdb/symfile.c +++ b/gdb/symfile.c @@ -2835,6 +2835,7 @@ allocate_symtab (struct compunit_symtab *cust, const char *filename, symtab->filename = objfile->intern (filename); symtab->filename_for_id = objfile->intern (filename_for_id); symtab->fullname = NULL; + symtab->source = NULL; symtab->set_language (deduce_language_from_filename (filename)); /* This can be very verbose with lots of headers. diff --git a/gdb/symmisc.c b/gdb/symmisc.c index 49b9674f77a..91d9ca906a5 100644 --- a/gdb/symmisc.c +++ b/gdb/symmisc.c @@ -810,9 +810,11 @@ maintenance_info_symtabs (const char *regexp, int from_tty) gdb_printf ("((struct symtab *) %s)\n", host_address_to_string (symtab)); gdb_printf ("\t fullname %s\n", - symtab->fullname != NULL + symtab->fullname != nullptr ? symtab->fullname : "(null)"); + if (symtab->source != nullptr) + gdb_printf ("\t source embedded in DWARF\n"); gdb_printf ("\t " "linetable ((struct linetable *) %s)\n", host_address_to_string @@ -955,15 +957,20 @@ maintenance_print_one_line_table (struct symtab *symtab, void *data) gdb_printf (_("compunit_symtab: %s ((struct compunit_symtab *) %s)\n"), symtab->compunit ()->name, host_address_to_string (symtab->compunit ())); + styled_string_s styled_symtab_fullname; + if (symtab->source) + styled_symtab_fullname = *styled_string (metadata_style.style (), + _("Source embedded in DWARF")); + else + styled_symtab_fullname = *styled_string (file_name_style.style (), + symtab_to_fullname (symtab)); gdb_printf (_("symtab: %ps ((struct symtab *) %s)\n"), - styled_string (file_name_style.style (), - symtab_to_fullname (symtab)), + &styled_symtab_fullname, host_address_to_string (symtab)); linetable = symtab->linetable (); gdb_printf (_("linetable: ((struct linetable *) %s):\n"), host_address_to_string (linetable)); - - if (linetable == NULL) + if (linetable == nullptr) gdb_printf (_("No line table.\n")); else if (linetable->nitems <= 0) gdb_printf (_("Line table has no lines.\n")); diff --git a/gdb/symtab.h b/gdb/symtab.h index bf9a3cfb79f..90c2f202390 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -1755,6 +1755,11 @@ struct symtab const char *filename; + /* When the contents of the source file were/are embedded with the + debugging info, this pointer will refer to that source. Can be nullptr + if the source for this source file is on disk. */ + const char *source; + /* Filename for this source file, used as an identifier to link with related objects such as associated macro_source_file objects. It must therefore match the name of any macro_source_file object created for this diff --git a/gdb/testsuite/gdb.dwarf2/dw2-lnct-source.c b/gdb/testsuite/gdb.dwarf2/dw2-lnct-source.c new file mode 100644 index 00000000000..86cdb9655b7 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-lnct-source.c @@ -0,0 +1,24 @@ +/* Copyright 2024 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + + +int +main (void) +{ /* main prologue */ + asm ("main_label: .global main_label"); + int m = 42; /* main assign m */ + asm ("main_end: .global main_end"); /* main end */ + return m; +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-lnct-source.exp b/gdb/testsuite/gdb.dwarf2/dw2-lnct-source.exp new file mode 100644 index 00000000000..73a06bd7b9c --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-lnct-source.exp @@ -0,0 +1,88 @@ +# Copyright 2024 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Check that GDB can honor LNCT_llvm_SOURCE. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +standard_testfile .c .S + +set asm_file [standard_output_file $srcfile2] + +set fp [open "${srcdir}/${subdir}/${srcfile}" r] +set srcfile_data [read $fp] +close $fp + +Dwarf::assemble $asm_file { + global srcdir subdir srcfile srcfile2 srcfile_data + declare_labels lines_label + + get_func_info main + + cu {} { + compile_unit { + {language @DW_LANG_C} + {name missing-file.c} + {stmt_list ${lines_label} DW_FORM_sec_offset} + } { + subprogram { + {external 1 flag} + {name main} + {low_pc $main_start addr} + {high_pc "$main_start + $main_len" addr} + } + } + } + + lines {version 5} lines_label { + set diridx [include_dir "${srcdir}/${subdir}"] + file_name "missing-file.c" $diridx "${srcfile_data}" + + program { + DW_LNS_set_file $diridx + DW_LNE_set_address $main_start + line [gdb_get_line_number "main prologue"] + DW_LNS_copy + + DW_LNE_set_address main_label + line [gdb_get_line_number "main assign m"] + DW_LNS_copy + + DW_LNE_set_address main_end + line [gdb_get_line_number "main end"] + DW_LNS_copy + + DW_LNE_end_sequence + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}] } { + return -1 +} + +if ![runto_main] { + return -1 +} + +set assign_m_line [gdb_get_line_number "main assign m"] +gdb_test "frame" ".*main \\\(\\\) at \[^\r\n\]*:$assign_m_line\r\n.*" +gdb_test "maintenance info symtabs missing-file.c" ".*source embedded in DWARF.*" +gdb_test "maintenance info line-table missing-file.c" ".*symtab: Source embedded in DWARF.*" +gdb_test "info source" ".*With embedded source.*" diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp index d085f835f07..b1f09eab3d3 100644 --- a/gdb/testsuite/lib/dwarf.exp +++ b/gdb/testsuite/lib/dwarf.exp @@ -2423,9 +2423,9 @@ namespace eval Dwarf { # Add a file name entry to the line table header's file names table. # # Return the index by which this entry can be referred to. - proc file_name {filename diridx} { + proc file_name {filename diridx { source "" } } { variable _line_file_names - lappend _line_file_names $filename $diridx + lappend _line_file_names $filename $diridx $source if { $Dwarf::_line_unit_version >= 5 } { return [expr [llength $_line_file_names] - 1] @@ -2481,7 +2481,7 @@ namespace eval Dwarf { } } - _op .byte 2 "file_name_entry_format_count" + _op .byte 3 "file_name_entry_format_count" _op .uleb128 1 \ "file_name_entry_format (content type code: DW_LNCT_path)" switch $_line_string_form { @@ -2494,15 +2494,21 @@ namespace eval Dwarf { "directory_entry_format (form: DW_FORM_line_strp)" } } + _op .uleb128 2 \ "file_name_entry_format (content type code: DW_LNCT_directory_index)" _op .uleb128 0x0f \ "file_name_entry_format (form: DW_FORM_udata)" - set nr_files [expr [llength $_line_file_names] / 2] + _op .uleb128 0x2001 \ + "file_name_entry_format (content type code: DW_LNCT_LLVM_SOURCE)" + _op .uleb128 0x08 \ + "file_name_entry_format (form: DW_FORM_string)" + + set nr_files [expr [llength $_line_file_names] / 3] _op .byte $nr_files "file_names_count" - foreach { filename diridx } $_line_file_names { + foreach { filename diridx source } $_line_file_names { switch $_line_string_form { string { _op .ascii [_quote $filename] @@ -2517,6 +2523,7 @@ namespace eval Dwarf { } } _op .uleb128 $diridx + _op .ascii [_quote [string map { "\"" "\\\"" "\n" "\\n" } $source]] } } else { foreach dirname $_line_include_dirs { @@ -2525,7 +2532,7 @@ namespace eval Dwarf { _op .byte 0 "Terminator (include_directories)" - foreach { filename diridx } $_line_file_names { + foreach { filename diridx source } $_line_file_names { _op .ascii [_quote $filename] _op .sleb128 $diridx _op .sleb128 0 "mtime" diff --git a/include/dwarf2.h b/include/dwarf2.h index b3d3731ee83..3823c041bab 100644 --- a/include/dwarf2.h +++ b/include/dwarf2.h @@ -289,6 +289,11 @@ enum dwarf_line_number_content_type DW_LNCT_size = 0x4, DW_LNCT_MD5 = 0x5, DW_LNCT_lo_user = 0x2000, + /* LLVM has implemented DW_LNCT_source (see + https://dwarfstd.org/issues/180201.1.html) as DW_LNCT_LLVM_SOURCE as + a vendor extension until the DWARF standard is updated (see + https://github.com/llvm/llvm-project/blob/08bb121835be432ac52372f92845950628ce9a4a/llvm/include/llvm/BinaryFormat/Dwarf.def#L1080 . */ + DW_LNCT_LLVM_SOURCE = 0x2001, DW_LNCT_hi_user = 0x3fff };