From patchwork Sat Oct 26 11:11:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 99649 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 476E13858406 for ; Sat, 26 Oct 2024 11:13:11 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 522FC3858C56 for ; Sat, 26 Oct 2024 11:11:40 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 522FC3858C56 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 522FC3858C56 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729941104; cv=none; b=hpb4f1RuAzO6DtBKaV65nmNHQcOSrrfCZzWNJd424z+b0kfrIFCr6xqQglwL4Y9LMEhbHugJQ6Uzwhl0r04ENuIlh5P3ixfGBpV+i7HltgyNULNBuM7ZQ7L9usaVrtge8gN4bMGdUCGngBcMAzLbSWXGpbvJTQcKDKehSbgLsh4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729941104; c=relaxed/simple; bh=TL1tswJdG6hYb34D4gNf0lYWfVaVG7Velef0nBOdLDQ=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=aOFuDtQxRgTXohLhWhlzBDBHJYjD9ErNFxM27+D35WrBvh7PwbPCW+O0UrVcibGrSGUKstycWSaCkId+e9HjA11lqHBcqrMxijqNGlWoH/DTAIKSJ3gYUUEv5RY68v84tTuPU0Nfig+uqZE/IGyOS7tZl6dpUyBGT2vt5hFJudg= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1729941100; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=prVTJ2Xrc7n1fRmpvQMyDNYg7VFU5V7ZY61G5zp4hnk=; b=BWRptokNZfyPct4exmqsr3ulbw243Uh1gmU99jW8k/Ze0QuO8VG6cPKke9ItLHtGwst/Y3 7dr56tD4tkON4gVTleUpgatiR554wZCYfkKDRU4SXqUMEUlzCisuI/9CahRkWzZMzhbR/w 6SR3smax8xkfBUbhg22Im4TRCarZI4s= Received: from mail-ej1-f69.google.com (mail-ej1-f69.google.com [209.85.218.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-541-VAQJri6MPD6Z_fBncz59lw-1; Sat, 26 Oct 2024 07:11:38 -0400 X-MC-Unique: VAQJri6MPD6Z_fBncz59lw-1 Received: by mail-ej1-f69.google.com with SMTP id a640c23a62f3a-a99fd71e777so223590266b.2 for ; Sat, 26 Oct 2024 04:11:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729941097; x=1730545897; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=prVTJ2Xrc7n1fRmpvQMyDNYg7VFU5V7ZY61G5zp4hnk=; b=fQta+dW6/DOPD5grkdCmck1ntqORjoMqjchtwD3JVbzKuAeGWaQkHlWrqvdfHpGaBe YyXVqAzndDkM1hEJ2w3MTkTv+JEa8n7OZ7r6QtfJiEBXmDZRQxK79vu2kNNB/I85gCgQ qRaO6GeweST4PipDzsdjZ862+ZO50oeL3Spi3/c+8jU5DjGQYS14qMPK1kM54XtHaeXu OcyCR3E+24MRzB4q+w+ulkxQWnu8eljnbPG6tLOwMpp7dK7q8+5KWOeAoSLe41BzgySp cvCx/GRK5s52QHGwFf65i8CB93o2uxbeTPEicr+lYhHYgWVVn/wnIO8P2H+K8IMqhDX/ s0uA== X-Gm-Message-State: AOJu0YwExma8CVskayeIN97TcV9QE4qrv9MNGNu9sF9539cqTMGJueEu ZO9demTTyAQz8djHiGaOUUJQA+KdwRMOZLOC+bJs2u/mtvd1hg6m2c7wqpPG6tNexYT7IRNd0Mo lE7vk+P4BKLoGC5+O9f+13w7TqdpYTE8I+mtgE90Kil8pSkIc7Kuvnp9g57FEzI79re3p8biFda AN2kNrarvTcA4b6udMn0wqajWlws9HX9Ub7G515wFs9qE= X-Received: by 2002:a17:907:9449:b0:a99:caf5:c897 with SMTP id a640c23a62f3a-a9de5d97ff7mr149456766b.20.1729941096093; Sat, 26 Oct 2024 04:11:36 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFh9uP8aHD94E/E+4xFrCkgDTWeCssHt49F1GpbfBF0QVcb/1Rtg4mkGP3qxWRNFKTC0NYuCA== X-Received: by 2002:a17:907:9449:b0:a99:caf5:c897 with SMTP id a640c23a62f3a-a9de5d97ff7mr149452066b.20.1729941095140; Sat, 26 Oct 2024 04:11:35 -0700 (PDT) Received: from localhost (92.40.184.80.threembb.co.uk. [92.40.184.80]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5cbb6348c88sm1444615a12.93.2024.10.26.04.11.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Oct 2024 04:11:34 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 4/5] gdb: improve GDB's ability to auto-load the exec for a core file Date: Sat, 26 Oct 2024 12:11:22 +0100 Message-Id: <0286175408bec9fbb26d5e69872ea8fd6f6e2735.1729940787.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, KAM_STOCKGEN, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, RCVD_IN_SBL_CSS, 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 GDB already has a limited mechanism for auto-loading the executable corresponding to a core file, this can be found in the function locate_exec_from_corefile_build_id in corelow.c. However, this approach uses the build-id of the core file to look in either the debug directory (for a symlink back to the executable) or by asking debuginfod. This is great, and works fine if the core file is a "system" binary, but often, when I'm debugging a core file, it's part of my development cycle, so there's no build-id symlink in the debug directory, and debuginfod doesn't know about the binary either, so GDB can't auto load the executable.... ... but the executable is right there! This commit builds on the earlier commits in this series to make GDB smarter. On GNU/Linux, when we parse the execution context from the core file (see linux-tdep.c), we already grab the command pointed to by AT_EXECFN. If this is an absolute path then GDB can use this to locate the executable, a build-id check ensures we've found the correct file. With this small change GDB suddenly becomes a lot better at auto-loading the executable for a core file. But we can do better! Often the AT_EXECFN is not an absolute path. If it is a relative path then we check for this path relative to the core file. This helps if a user does something like: $ ./build/bin/some_prog Aborted (core dumped) $ gdb -c corefile In this case the core file in the current directory will have an AT_EXECFN value of './build/bin/some_prog', so if we look for that path relative to the location of the core file this might result in a hit, again, a build-id check ensures we found the right file. But we can do better still! What if the user moves the core file? Or the user is using some tool to manage core files (e.g. the systemd core file management tool), and the user downloads the core file to a location from which the relative path no longer works? Well in this case we can make use of the core file's mapped file information (the NT_FILE note). The executable will be included in the mapped file list, and the path within the mapped file list will be an absolute path. We can search for mapped file information based on an address within the mapped file, and the auxv vector happens to include an AT_ENTRY value, which is the entry address in the main executable. If we look up the mapped file containing this address we'll have the absolute path to the main executable, a build-id check ensures this really is the file we're looking for. It might be tempting to jump straight to the third approach, however, there is one small downside to the third approach: if the executable is a symlink then the AT_EXECFN string will be the name of the symlink, that is, the thing the user asked to run. The mapped file entry will be the name of the actual file, i.e. the symlink target. When we auto-load the executable based on the third approach, the file loaded might have a different name to that which the user expects, though the build-id check (almost) guarantees that we've loaded the correct binary. But there's one more thing we can check for! If the user has placed the core file and the executable into a directory together, for example, as might happen with a bug report, then neither the absolute path check, nor the relative patch check will find the executable. So GDB will also look for a file with the right name in the same directory as the core file. Again, a build-id check is performed to ensure we find the correct file. Of course, it's still possible that GDB is unable to find the executable using any of these approaches. In this case, nothing changes, GDB will check in the debug info directory for a build-id based link back to the executable, and if that fails, GDB will ask debuginfod for the executable. If this all fails, then, as usual, the user is able to load the correct executable with the 'file' command, but hopefully, this should be needed far less from now on. --- gdb/arch-utils.h | 25 +- gdb/corelow.c | 141 ++++++++-- gdb/linux-tdep.c | 22 ++ gdb/testsuite/gdb.base/corefile-find-exec.c | 25 ++ gdb/testsuite/gdb.base/corefile-find-exec.exp | 242 ++++++++++++++++++ 5 files changed, 438 insertions(+), 17 deletions(-) create mode 100644 gdb/testsuite/gdb.base/corefile-find-exec.c create mode 100644 gdb/testsuite/gdb.base/corefile-find-exec.exp diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index 1c33bfb4704..fb4a3ef9c5b 100644 --- a/gdb/arch-utils.h +++ b/gdb/arch-utils.h @@ -22,6 +22,7 @@ #include "gdbarch.h" #include "gdbsupport/environ.h" +#include "filenames.h" class frame_info_ptr; struct minimal_symbol; @@ -87,15 +88,23 @@ struct core_file_exec_context never be nullptr. Only call this constructor if all the arguments have been collected successfully, i.e. if the EXEC_NAME could be found but not ARGV then use the no-argument constructor to create an - empty context object. */ + empty context object. + + The EXEC_FILENAME must be the absolute filename of the executable + that generated this core file, or nullptr if the absolute filename + is not known. */ core_file_exec_context (gdb::unique_xmalloc_ptr exec_name, + gdb::unique_xmalloc_ptr exec_filename, std::vector> argv, std::vector> envp) : m_exec_name (std::move (exec_name)), + m_exec_filename (std::move (exec_filename)), m_arguments (std::move (argv)), m_environment (std::move (envp)) { gdb_assert (m_exec_name != nullptr); + gdb_assert (exec_filename == nullptr + || IS_ABSOLUTE_PATH (exec_filename.get ())); } /* Create a default context object. In its default state a context @@ -112,6 +121,13 @@ struct core_file_exec_context const char *execfn () const { return m_exec_name.get (); } + /* Return the absolute path to the executable if known. This might + return nullptr even when execfn() returns a non-nullptr value. + Additionally, the file referenced here might have a different name + than the file returned by execfn if execfn is a symbolic link. */ + const char *exec_filename () const + { return m_exec_filename.get (); } + /* Return the vector of inferior arguments as extracted from the core file. This does not include argv[0] (the executable name) for that see the execfn() function. */ @@ -127,6 +143,13 @@ struct core_file_exec_context if no executable name is found. */ gdb::unique_xmalloc_ptr m_exec_name; + /* Full filename to the executable that was actually executed. The name + within EXEC_FILENAME might not match what the user typed, e.g. if the + user typed ./symlinked_name which is a symlink to /tmp/real_name then + this is going to contain '/tmp/realname' while EXEC_NAME above will + contain './symlinkedname'. */ + gdb::unique_xmalloc_ptr m_exec_filename; + /* List of arguments. Doesn't include argv[0] which is the executable name, for this look at m_exec_name field. */ std::vector> m_arguments; diff --git a/gdb/corelow.c b/gdb/corelow.c index a0129f84b1c..272b86b6f33 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -828,18 +828,117 @@ rename_vmcore_idle_reg_sections (bfd *abfd, inferior *inf) replacement_lwpid_str.c_str ()); } +/* Use CTX to try and find (and open) the executable file for the core file + CBFD. BUILD_ID is the build-id for CBFD which was already extracted by + our caller. + + Will return the opened executable or nullptr if the executable couldn't + be found. */ + +static gdb_bfd_ref_ptr +locate_exec_from_corefile_exec_context (bfd *cbfd, + const bfd_build_id *build_id, + const core_file_exec_context &ctx) +{ + /* CTX must be valid, and a valid context has an execfn() string. */ + gdb_assert (ctx.valid ()); + gdb_assert (ctx.execfn () != nullptr); + + /* EXEC_NAME will be the command used to start the inferior. This might + not be an absolute path (but could be). */ + const char *exec_name = ctx.execfn (); + + /* Function to open FILENAME and check if its build-id matches BUILD_ID + from this enclosing scope. Returns the open BFD for filename if the + FILENAME has a matching build-id, otherwise, returns nullptr. */ + const auto open_and_check_build_id + = [&build_id] (const char *filename) -> gdb_bfd_ref_ptr + { + /* Try to open a file. If this succeeds then we still need to perform + a build-id check. */ + gdb_bfd_ref_ptr execbfd = gdb_bfd_open (filename, gnutarget); + + /* We managed to open a file, but if it's build-id doesn't match + BUILD_ID then we just cannot trust it's the right file. */ + if (execbfd != nullptr) + { + const bfd_build_id *other_build_id = build_id_bfd_get (execbfd.get ()); + + if (other_build_id == nullptr + || !build_id_equal (other_build_id, build_id)) + execbfd = nullptr; + } + + return execbfd; + }; + + gdb_bfd_ref_ptr execbfd; + + /* If EXEC_NAME is absolute then try to open it now. Otherwise, see if + EXEC_NAME is a relative path from the location of the core file. This + is just a guess, the executable might not be here, but we still rely + on a build-id match in order to accept any executable we find; we + don't accept something just because it happens to be in the right + location. */ + if (IS_ABSOLUTE_PATH (exec_name)) + execbfd = open_and_check_build_id (exec_name); + else + { + std::string p = (ldirname (bfd_get_filename (cbfd)) + + '/' + + exec_name); + execbfd = open_and_check_build_id (p.c_str ()); + } + + /* If we haven't found the executable yet, then try checking to see if + the executable is in the same directory as the core file. Again, + there's no reason why this should be the case, but it's worth a try, + and the build-id check should ensure we don't use an invalid file if + we happen to find one. */ + if (execbfd == nullptr) + { + const char *base_name = lbasename (exec_name); + std::string p = (ldirname (bfd_get_filename (cbfd)) + + '/' + + base_name); + execbfd = open_and_check_build_id (p.c_str ()); + } + + /* If the above didn't provide EXECBFD then try the exec_filename from + the context. This will be an absolute filename which the gdbarch code + figured out from the core file. In some cases the gdbarch code might + not be able to figure out a suitable absolute filename though. */ + if (execbfd == nullptr && ctx.exec_filename () != nullptr) + { + gdb_assert (IS_ABSOLUTE_PATH (ctx.exec_filename ())); + + /* Try to open a file. If this succeeds then we still need to + perform a build-id check. */ + execbfd = open_and_check_build_id (ctx.exec_filename ()); + } + + return execbfd; +} + /* Locate (and load) an executable file (and symbols) given the core file BFD ABFD. */ static void -locate_exec_from_corefile_build_id (bfd *abfd, int from_tty) +locate_exec_from_corefile_build_id (bfd *abfd, + const core_file_exec_context &ctx, + int from_tty) { const bfd_build_id *build_id = build_id_bfd_get (abfd); if (build_id == nullptr) return; - gdb_bfd_ref_ptr execbfd - = find_objfile_by_build_id (build_id, abfd->filename); + gdb_bfd_ref_ptr execbfd; + + if (ctx.valid ()) + execbfd = locate_exec_from_corefile_exec_context (abfd, build_id, ctx); + + if (execbfd == nullptr) + execbfd = find_objfile_by_build_id (build_id, abfd->filename); if (execbfd != nullptr) { @@ -908,13 +1007,6 @@ core_target_open (const char *arg, int from_tty) validate_files (); - /* If we have no exec file, try to set the architecture from the - core file. We don't do this unconditionally since an exec file - typically contains more information that helps us determine the - architecture than a core file. */ - if (!current_program_space->exec_bfd ()) - set_gdbarch_from_file (current_program_space->core_bfd ()); - current_inferior ()->push_target (std::move (target_holder)); switch_to_no_thread (); @@ -969,9 +1061,31 @@ core_target_open (const char *arg, int from_tty) switch_to_thread (thread); } + /* In order to parse the exec context from the core file the current + inferior needs to have a suitable gdbarch set. If an exec file is + loaded then the gdbarch will have been set based on the exec file, but + if not, ensure we have a suitable gdbarch in place now. */ + if (current_program_space->exec_bfd () == nullptr) + current_inferior ()->set_arch (target->core_gdbarch ()); + + /* See if the gdbarch can find the executable name and argument list from + the core file. */ + core_file_exec_context ctx + = gdbarch_core_parse_exec_context (target->core_gdbarch (), + current_program_space->core_bfd ()); + + /* If we don't have an executable loaded then see if we can locate one + based on the core file. */ if (current_program_space->exec_bfd () == nullptr) locate_exec_from_corefile_build_id (current_program_space->core_bfd (), - from_tty); + ctx, from_tty); + + /* If we have no exec file, try to set the architecture from the + core file. We don't do this unconditionally since an exec file + typically contains more information that helps us determine the + architecture than a core file. */ + if (current_program_space->exec_bfd () == nullptr) + set_gdbarch_from_file (current_program_space->core_bfd ()); post_create_inferior (from_tty); @@ -989,11 +1103,6 @@ core_target_open (const char *arg, int from_tty) exception_print (gdb_stderr, except); } - /* See if the gdbarch can find the executable name and argument list from - the core file. */ - core_file_exec_context ctx - = gdbarch_core_parse_exec_context (target->core_gdbarch (), - current_program_space->core_bfd ()); if (ctx.valid ()) { std::string args; diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index d981824f081..2632d143569 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -2083,7 +2083,29 @@ linux_corefile_parse_exec_context_1 (struct gdbarch *gdbarch, bfd *cbfd) if (execfn == nullptr) return {}; + /* When the core-file was loaded GDB processed the file backed mappings + (from the NT_FILE note). One of these should have been for the + executable. The AT_EXECFN string might not be an absolute path, but + the path in NT_FILE will be absolute, though if AT_EXECFN is a + symlink, then the NT_FILE entry will point to the actual file, not the + symlink. + + Use the AT_ENTRY address to look for the NT_FILE entry which contains + that address, this should be the executable. */ + gdb::unique_xmalloc_ptr exec_filename; + CORE_ADDR exec_entry_addr; + if (target_auxv_search (contents, current_inferior ()->top_target (), + gdbarch, AT_ENTRY, &exec_entry_addr) == 1) + { + std::optional info + = core_target_find_mapped_file (nullptr, exec_entry_addr); + if (info.has_value () && !info->filename ().empty () + && IS_ABSOLUTE_PATH (info->filename ().c_str ())) + exec_filename = make_unique_xstrdup (info->filename ().c_str ()); + } + return core_file_exec_context (std::move (execfn), + std::move (exec_filename), std::move (arguments), std::move (environment)); } diff --git a/gdb/testsuite/gdb.base/corefile-find-exec.c b/gdb/testsuite/gdb.base/corefile-find-exec.c new file mode 100644 index 00000000000..ed4df606a2d --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-find-exec.c @@ -0,0 +1,25 @@ +/* This testcase is part of GDB, the GNU debugger. + + 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 . */ + +#include + +int +main (int argc, char **argv) +{ + abort (); + return 0; +} diff --git a/gdb/testsuite/gdb.base/corefile-find-exec.exp b/gdb/testsuite/gdb.base/corefile-find-exec.exp new file mode 100644 index 00000000000..40324c1f01c --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-find-exec.exp @@ -0,0 +1,242 @@ +# 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 GDB's ability to auto-load the executable based on the file +# names extracted from the core file. +# +# Currently, only Linux supports reading full executable and arguments +# from a core file. +require {istarget *-linux*} + +standard_testfile + +if {[build_executable $testfile.exp $testfile $srcfile {debug build-id}] == -1} { + untested "failed to compile" + return -1 +} + +# Load the COREFILE and confirm that GDB auto-loads the executable. +# The symbols should be read from SYMBOL_FILE and the core file should +# be reported as generated by GEN_FROM_FILE. +proc test_load { corefile symbol_file gen_from_file } { + clean_restart + set saw_generated_line false + set saw_reading_symbols false + + gdb_test_multiple "core-file $corefile" "load core file" { + + -re "^Reading symbols from [string_to_regexp $symbol_file]\\.\\.\\.\r\n" { + set saw_reading_symbols true + exp_continue + } + + -re "^Core was generated by `[string_to_regexp $gen_from_file]'\\.\r\n" { + set saw_generated_line true + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert { $saw_generated_line && $saw_reading_symbols} \ + $gdb_test_name + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } +} + +with_test_prefix "absolute path" { + # Generate a core file, this uses an absolute path to the + # executable. + with_test_prefix "to file" { + set corefile [core_find $binfile] + if {$corefile == ""} { + untested "unable to create corefile" + return 0 + } + set corefile_1 "$binfile.1.core" + remote_exec build "mv $corefile $corefile_1" + + test_load $corefile_1 $binfile $binfile + } + + # And create a symlink, and repeat the test using an absolute path + # to the symlink. + with_test_prefix "to symlink" { + set symlink_name "symlink_1" + set symlink [standard_output_file $symlink_name] + + with_cwd [standard_output_file ""] { + remote_exec build "ln -s ${testfile} $symlink_name" + } + + set corefile [core_find $symlink] + if {$corefile == ""} { + untested "unable to create corefile" + return 0 + } + set corefile_2 "$binfile.2.core" + remote_exec build "mv $corefile $corefile_2" + + test_load $corefile_2 $symlink $symlink + } + + # Like the previous test, except this time, delete the symlink + # after generating the core file. GDB should be smart enough to + # figure out that we can use the underlying TESTFILE binary. + with_test_prefix "to deleted symlink" { + set symlink_name "symlink_2" + set symlink [standard_output_file $symlink_name] + + with_cwd [standard_output_file ""] { + remote_exec build "ln -s ${testfile} $symlink_name" + } + + set corefile [core_find $symlink] + if {$corefile == ""} { + untested "unable to create corefile" + return 0 + } + set corefile_3 "$binfile.3.core" + remote_exec build "mv $corefile $corefile_3" + + remote_exec build "rm -f $symlink" + + test_load $corefile_3 $binfile $symlink + } + + # Generate the core file with an absolute path to the executable, + # but move the core file and executable into a single directory + # together so GDB can't use the absolute path to find the + # executable. + # + # GDB should still find the executable though, but looking in the + # same directory as the core file. + with_test_prefix "in side directory" { + set binfile_2 [standard_output_file ${testfile}_2] + remote_exec build "cp $binfile $binfile_2" + + set corefile [core_find $binfile_2] + if {$corefile == ""} { + untested "unable to create corefile" + return 0 + } + set corefile_4 "$binfile.4.core" + remote_exec build "mv $corefile $corefile_4" + + set side_dir [standard_output_file side_dir] + remote_exec build "mkdir -p $side_dir" + remote_exec build "mv $binfile_2 $side_dir" + remote_exec build "mv $corefile_4 $side_dir" + + set relocated_corefile_4 [file join $side_dir [file tail $corefile_4]] + set relocated_binfile_2 [file join $side_dir [file tail $binfile_2]] + test_load $relocated_corefile_4 $relocated_binfile_2 $binfile_2 + } +} + +with_test_prefix "relative path" { + # Generate a core file using relative a path. We ned to work + # around the core_find proc a little here. The core_find proc + # creates a sub-directory using standard_output_file and runs the + # test binary from inside that directory. + # + # Usually core_find is passed an absolute path, so thre's no + # problem, but we want to pass a relative path. + # + # So setup a directory structure like this: + # + # corefile-find-exec/ + # reldir/ + # + # workdir/ + # + # Place a copy of BINFILE in 'reldir/' and switch to workdir, use + # core_find which will create a sibling directory of workdir, and + # run the relative path from there. We then move the generated + # core file back into 'workdir/', this leaves a tree like: + # + # corefile-find-exec/ + # reldir/ + # + # workdir/ + # + # + # Now we can ask GDB to open the core file, if all goes well GDB + # should make use of the relative path encoded in the core file to + # locate the executable in 'reldir/'. + # + # We also setup a symlink in 'reldir' that points to the + # executable and repeat the test, but this time executing the + # symlink. + set reldir_name "reldir" + set reldir [standard_output_file $reldir_name] + remote_exec build "mkdir -p $reldir" + + set alt_testfile "alt_${testfile}" + set binfile_3 "$reldir/${alt_testfile}" + remote_exec build "cp $binfile $binfile_3" + + set symlink_2 "symlink_2" + with_cwd $reldir { + remote_exec build "ln -s ${alt_testfile} ${symlink_2}" + } + + set work_dir [standard_output_file "workdir"] + remote_exec build "mkdir -p $work_dir" + + set rel_path_to_file "../${reldir_name}/${alt_testfile}" + set rel_path_to_symlink_2 "../${reldir_name}/${symlink_2}" + + with_cwd $work_dir { + with_test_prefix "to file" { + set corefile [core_find $rel_path_to_file] + if {$corefile == ""} { + untested "unable to create corefile" + return 0 + } + set corefile_5 "${work_dir}/${testfile}.5.core" + remote_exec build "mv $corefile $corefile_5" + + test_load $corefile_5 \ + [file join $work_dir $rel_path_to_file] \ + $rel_path_to_file + } + + with_test_prefix "to symlink" { + set corefile [core_find $rel_path_to_symlink_2] + if {$corefile == ""} { + untested "unable to create corefile" + return 0 + } + set corefile_6 "${work_dir}/${testfile}.6.core" + remote_exec build "mv $corefile $corefile_6" + + test_load $corefile_6 \ + [file join $work_dir $rel_path_to_symlink_2] \ + $rel_path_to_symlink_2 + } + + # Move the core file. Now the relative path doesn't work so + # we instead rely on GDB to use information about the mapped + # files to help locate the executable. + with_test_prefix "with moved corefile" { + set corefile_7 [standard_output_file "${testfile}.7.core"] + remote_exec build "cp $corefile_6 $corefile_7" + test_load $corefile_7 $binfile_3 $rel_path_to_symlink_2 + } + } +}