/* Disable any breakpoints and tracepoints that are in SOLIB upon
notification of unloaded_shlib. Only apply to enabled breakpoints,
- disabled ones can just stay disabled. */
+ disabled ones can just stay disabled.
+
+ When STILL_IN_USE is true, SOLIB hasn't really been unmapped from
+ the inferior. In this case, don't disable anything. */
static void
-disable_breakpoints_in_unloaded_shlib (program_space *pspace, const solib &solib)
+disable_breakpoints_in_unloaded_shlib (program_space *pspace, const solib &solib,
+ bool still_in_use)
{
+ if (still_in_use)
+ return;
+
bool disabled_shlib_breaks = false;
for (bp_location *loc : all_bp_locations ())
}
static void
-bsd_uthread_solib_unloaded (program_space *pspace, const solib &so)
+bsd_uthread_solib_unloaded (program_space *pspace, const solib &so,
+ bool still_in_use)
{
- if (bsd_uthread_solib_name.empty ())
+ if (bsd_uthread_solib_name.empty () || still_in_use)
return;
if (so.so_original_name == bsd_uthread_solib_name)
/* See interps.h. */
void
-interps_notify_solib_unloaded (const solib &so)
+interps_notify_solib_unloaded (const solib &so, bool still_in_use)
{
- interps_notify (&interp::on_solib_unloaded, so);
+ interps_notify (&interp::on_solib_unloaded, so, still_in_use);
}
/* See interps.h. */
/* Notify the interpreter that solib SO has been loaded. */
virtual void on_solib_loaded (const solib &so) {}
- /* Notify the interpreter that solib SO has been unloaded. */
- virtual void on_solib_unloaded (const solib &so) {}
+ /* Notify the interpreter that solib SO has been unloaded. When
+ STILL_IN_USE is true, the objfile backing SO is still in use,
+ this indicates that SO was loaded multiple times, but only mapped
+ in once (the mapping was reused). */
+ virtual void on_solib_unloaded (const solib &so, bool still_in_use) {}
/* Notify the interpreter that a command it is executing is about to cause
the inferior to proceed. */
/* Notify all interpreters that solib SO has been loaded. */
extern void interps_notify_solib_loaded (const solib &so);
-/* Notify all interpreters that solib SO has been unloaded. */
-extern void interps_notify_solib_unloaded (const solib &so);
+/* Notify all interpreters that solib SO has been unloaded. When
+ STILL_IN_USE is true, the objfile backing SO is still in use, this
+ indicates that SO was loaded multiple times, but only mapped in
+ once (the mapping was reused). */
+extern void interps_notify_solib_unloaded (const solib &so, bool still_in_use);
/* Notify all interpreters that the selected traceframe changed.
}
void
-mi_interp::on_solib_unloaded (const solib &solib)
+mi_interp::on_solib_unloaded (const solib &solib, bool still_in_use)
{
ui_out *uiout = this->interp_ui_out ();
const char *format) override;
void on_target_resumed (ptid_t ptid) override;
void on_solib_loaded (const solib &so) override;
- void on_solib_unloaded (const solib &so) override;
+ void on_solib_unloaded (const solib &so, bool still_in_use) override;
void on_about_to_proceed () override;
void on_traceframe_changed (int tfnum, int tpnum) override;
void on_tsv_created (const trace_state_variable *tsv) override;
/* The shared library SOLIB has been unloaded from program space PSPACE.
Note when gdb calls this observer, the library's symbols have not
been unloaded yet, and thus are still available. */
-extern observable<program_space *, const solib &/* solib */> solib_unloaded;
+extern observable<program_space *, const solib &/* solib */,
+ bool /* still_in_use */> solib_unloaded;
/* The symbol file specified by OBJFILE has been loaded. */
extern observable<struct objfile */* objfile */> new_objfile;
gdb::observers::solib_loaded.notify (so);
}
-/* Notify interpreters and observers that solib SO has been unloaded. */
+/* Notify interpreters and observers that solib SO has been unloaded.
+ When STILL_IN_USE is true, the objfile backing SO is still in use,
+ this indicates that SO was loaded multiple times, but only mapped
+ in once (the mapping was reused). */
static void
-notify_solib_unloaded (program_space *pspace, const solib &so)
+notify_solib_unloaded (program_space *pspace, const solib &so,
+ bool still_in_use)
{
- interps_notify_solib_unloaded (so);
- gdb::observers::solib_unloaded.notify (pspace, so);
+ interps_notify_solib_unloaded (so, still_in_use);
+ gdb::observers::solib_unloaded.notify (pspace, so, still_in_use);
}
/* See solib.h. */
/* If it's not on the inferior's list, remove it from GDB's tables. */
else
{
+ bool still_in_use
+ = (gdb_iter->objfile != nullptr
+ && solib_used (current_program_space, *gdb_iter));
+
/* Notify any observer that the shared object has been
unloaded before we remove it from GDB's tables. */
- notify_solib_unloaded (current_program_space, *gdb_iter);
-
- current_program_space->deleted_solibs.push_back (gdb_iter->so_name);
+ notify_solib_unloaded (current_program_space, *gdb_iter,
+ still_in_use);
/* Unless the user loaded it explicitly, free SO's objfile. */
if (gdb_iter->objfile != nullptr
&& !(gdb_iter->objfile->flags & OBJF_USERLOADED)
- && !solib_used (current_program_space, *gdb_iter))
+ && !still_in_use)
gdb_iter->objfile->unlink ();
+ current_program_space->deleted_solibs.push_back (gdb_iter->so_name);
+
/* Some targets' section tables might be referring to
sections from so.abfd; remove them. */
current_program_space->remove_target_sections (&*gdb_iter);
for (solib &so : pspace->so_list)
{
- notify_solib_unloaded (pspace, so);
+ bool still_in_use
+ = (so.objfile != nullptr && solib_used (pspace, so));
+
+ notify_solib_unloaded (pspace, so, still_in_use);
pspace->remove_target_sections (&so);
};
set bp_inc [gdb_get_line_number "bp.inc" $srcfile_lib]
set bp_main [gdb_get_line_number "bp.main" $srcfile]
+# Figure out the file name for the dynamic linker.
+set dyln_name [section_get $binfile .interp]
+if { $dyln_name eq "" } {
+ unsupported "couldn't find dynamic linker name"
+ return
+}
+
+# Return true if FILENAME is the dynamic linker. Otherwise return false.
+proc is_dyln { filename } {
+ return [expr {$filename eq $::dyln_name}]
+}
+
# Check that 'info shared' show NUM occurrences of DSO.
proc check_dso_count { dso num } {
global gdb_prompt hex
}
}
+# Run 'info sharedlibrary' and count the number of mappings that look
+# like they might be the dynamic linker. This will only work for
+# Linux right now.
+proc get_dyld_info {} {
+ if { ![istarget *-linux*] } {
+ return [list 0 ""]
+ }
+
+ set dyld_count 0
+ set dyld_start_addr ""
+ gdb_test_multiple "info sharedlibrary" "" {
+ -re "From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library\r\n" {
+ exp_continue
+ }
+ -re "^($::hex)\\s+$::hex\\s+\[^/\]+(/\[^\r\n\]+)\r\n" {
+ set addr $expect_out(1,string)
+ set lib $expect_out(2,string)
+
+ if { [is_dyln $lib] } {
+ # This looks like it might be the dynamic linker.
+ incr dyld_count
+ if { $dyld_start_addr eq "" } {
+ set dyld_start_addr $addr
+ } elseif { $dyld_start_addr ne $addr } {
+ set dyld_start_addr "MULTIPLE"
+ }
+ }
+
+ exp_continue
+ }
+ -re "\\(\\*\\): Shared library is missing debugging information\\.\r\n" {
+ exp_continue
+ }
+ -re "^$::gdb_prompt $" {
+ }
+ }
+
+ if { $dyld_start_addr eq "MULTIPLE" } {
+ set dyld_start_addr ""
+ }
+
+ return [list $dyld_count $dyld_start_addr]
+}
+
+# The inferior for this test causes the dynamic linker to be appear
+# multiple times in the inferior's shared library list, but (at least
+# with glibc), the dynamic linker is really only mapped in once. That
+# is, each of the dynamic linker instances that appear in the 'info
+# sharedlibrary' output, will have the same address range.
+#
+# This test creates a user breakpoint in the dynamic linker, and then
+# runs over the dlcose calls, which unmap all but one of the dynamic
+# linker instances.
+#
+# The expectation is that the user breakpoint in the dynamic linker
+# should still be active. Older versions of GDB had a bug where the
+# breakpoint would become pending.
+proc_with_prefix test_solib_unmap_events { } {
+
+ # This test relies on finding the dynamic linker library, and is
+ # currently written assuming Linux.
+ if { ![istarget *-linux*] } {
+ unsupport "cannot find dynamic linker library on this target"
+ return
+ }
+
+ clean_restart $::binfile
+
+ if { ![runto_main] } {
+ return
+ }
+
+ # Check that before any of our dlopen/dlmopen calls, we can find a
+ # single copy of the dynamic linker in the shared library list.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ if { $dyld_count != 1 } {
+ unsupported "initial dyld state appears strange"
+ return
+ }
+
+ # Continue the inferior until all solib are loaded.
+ set alarm_lineno [gdb_get_line_number "alarm" $::srcfile]
+ gdb_breakpoint ${::srcfile}:${alarm_lineno}
+ gdb_continue_to_breakpoint "all solib are now loaded"
+
+ # Check that we have multiple copies of dynamic linker loaded, and
+ # that the dynamic linker is only loaded at a single address.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ set dyld_start_addr [lindex $dyld_info 1]
+
+ # If we didn't find a suitable dynamic linker address, or we
+ # didn't find multiple copies of the dynamic linker, then
+ # something has gone wrong with the test setup.
+ if { $dyld_count < 2 } {
+ unsupported "multiple copies of the dynamic linker not found"
+ return
+ }
+ if { $dyld_start_addr eq "" } {
+ unsupported "unable to find suitable dynamic linker start address"
+ return
+ }
+
+ # Check the address we found is (likely) writable.
+ gdb_test_multiple "x/1i $dyld_start_addr" "check b/p address" {
+ -re -wrap "Cannot access memory at address \[^\r\n\]+" {
+ unsupported "dynamic linker address is not accessible"
+ return
+ }
+ -re -wrap "" {
+ }
+ }
+
+ # Create a breakpoint within the dynamic linker. It is pretty unlikely
+ # that this breakpoint will ever be hit, but just in case it is, make it
+ # conditional, with a condition that will never be true. All we really
+ # care about for this test is whether the breakpoint will be made
+ # pending or not (it should not).
+ gdb_test "break *$dyld_start_addr if (0)" \
+ "Breakpoint $::decimal at $::hex\[^\r\n\]+" \
+ "create breakpoint within dynamic linker"
+ set bpnum [get_integer_valueof "\$bpnum" INVALID "get bpnum"]
+
+ # Now continue until the 'bp.main' location, this will unload some
+ # copies, but not all copies, of the dynamic linker.
+ gdb_test "print wait_for_gdb = 0" " = 0"
+ set bp_main [gdb_get_line_number "bp.main" $::srcfile]
+
+ gdb_breakpoint $::srcfile:$bp_main
+ gdb_continue_to_breakpoint "stop at bp.main"
+
+ # At one point, GDB would incorrectly mark the breakpoints in the
+ # dynamic linker as pending when some instances of the library were
+ # unloaded, despite there really only being one copy of the dynamic
+ # linker actually loaded into the inferior's address space.
+ gdb_test_multiple "info breakpoints $bpnum" "check b/p status" {
+ -re -wrap "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+\\*$::hex\\s*\r\n\\s+stop only if \\(0\\)" {
+ fail $gdb_test_name
+ }
+
+ -re -wrap "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$::hex\\s*\[^\r\n\]+\r\n\\s+stop only if \\(0\\)" {
+ pass $gdb_test_name
+ }
+ }
+
+ # With all the dlclose calls now complete, we should be back to a
+ # single copy of the dynamic linker.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ gdb_assert { $dyld_count == 1 } \
+ "one dynamic linker found after dlclose calls"
+}
+
# Run the actual tests.
test_dlmopen_no_attach
test_dlmopen_with_attach
+test_solib_unmap_events
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2021-2025 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+__attribute__((visibility ("default")))
+int gdb_dlmopen_glob = 1;
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2021-2025 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+extern int gdb_dlmopen_glob;
+
+__attribute__((visibility ("default")))
+int
+inc (int n)
+{
+ int amount = gdb_dlmopen_glob;
+ return n + amount; /* bp.inc. */
+}
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2021-2025 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#define _GNU_SOURCE
+#include <dlfcn.h>
+#include <stddef.h>
+#include <assert.h>
+#include <unistd.h>
+
+int
+main (void)
+{
+ void *handle[4];
+ int (*fun) (int);
+ Lmid_t lmid;
+ int dl;
+
+ handle[0] = dlmopen (LM_ID_NEWLM, DSO1_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[0] != NULL);
+
+ dlinfo (handle[0], RTLD_DI_LMID, &lmid);
+
+ handle[1] = dlopen (DSO1_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[1] != NULL);
+
+ handle[2] = dlmopen (LM_ID_NEWLM, DSO1_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[2] != NULL);
+
+ handle[3] = dlmopen (lmid, DSO2_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[3] != NULL); /* bp.loaded */
+
+ for (dl = 0; dl < 4; ++dl)
+ {
+ fun = dlsym (handle[dl], "inc");
+ assert (fun != NULL);
+
+ fun (42);
+
+ dlclose (handle[dl]);
+ }
+
+ return 0; /* bp.main */
+}
--- /dev/null
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2024-2025 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 <http://www.gnu.org/licenses/>.
+
+# MI tests related to loading shared libraries into different namespaces
+# with dlmopen(). The source files for this test are copied (almost)
+# verbatim from the gdb.base/dlmopen.exp test.
+
+load_lib "mi-support.exp"
+
+require allow_dlmopen_tests
+
+standard_testfile .c -lib.c -lib-dep.c
+
+set basename_lib dlmopen-lib
+set srcfile_lib $srcfile2
+set binfile_lib1 [standard_output_file $basename_lib.1.so]
+set binfile_lib2 [standard_output_file $basename_lib.2.so]
+set srcfile_lib_dep $srcfile3
+set binfile_lib_dep [standard_output_file $basename_lib-dep.so]
+
+if { [build_executable "build shlib dep" $binfile_lib_dep $srcfile_lib_dep \
+ {debug shlib}] == -1 } {
+ return
+}
+
+if { [build_executable "build shlib" $binfile_lib1 $srcfile_lib \
+ [list debug shlib_load shlib libs=$binfile_lib_dep]] == -1 } {
+ return
+}
+
+if { [build_executable "build shlib" $binfile_lib2 $srcfile_lib \
+ [list debug shlib_load shlib libs=$binfile_lib_dep]] == -1 } {
+ return
+}
+
+if { [build_executable "failed to build" $testfile $srcfile \
+ [list additional_flags=-DDSO1_NAME=\"$binfile_lib1\" \
+ additional_flags=-DDSO2_NAME=\"$binfile_lib2\" \
+ shlib_load debug]] } {
+ return
+}
+
+# Figure out the file name for the dynamic linker.
+set dyln_name [section_get $binfile .interp]
+if { $dyln_name eq "" } {
+ unsupported "couldn't find dynamic linker name"
+ return
+}
+
+# Some source locations needed by the tests.
+set bp_main [gdb_get_line_number "bp.main" $srcfile]
+set bp_loaded [gdb_get_line_number "bp.loaded" $srcfile]
+
+# Return true if FILENAME is the dynamic linker. Otherwise return false.
+proc is_dyln { filename } {
+ return [expr {$filename eq $::dyln_name}]
+}
+
+# Run 'info sharedlibrary' and count the number of mappings that look
+# like they might be the dynamic linker. This will only work for
+# Linux right now.
+proc get_dyld_info {} {
+ if { ![istarget *-linux*] } {
+ return [list 0 ""]
+ }
+
+ set dyld_count 0
+ set dyld_start_addr ""
+ gdb_test_multiple "info sharedlibrary" "" {
+ -re "~\"From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library\\\\n\"\r\n" {
+ exp_continue
+ }
+ -re "^~\"($::hex)\\s+$::hex\\s+\[^/\]+(/\[^\r\n\]+)\\\\n\"\r\n" {
+ set addr $expect_out(1,string)
+ set lib $expect_out(2,string)
+
+ if { [is_dyln $lib] } {
+ # This looks like it might be the dynamic linker.
+ incr dyld_count
+ if { $dyld_start_addr eq "" } {
+ set dyld_start_addr $addr
+ } elseif { $dyld_start_addr ne $addr } {
+ set dyld_start_addr "MULTIPLE"
+ }
+ }
+
+ exp_continue
+ }
+ -re "~\"\\(\\*\\): Shared library is missing debugging information\\.\\\\n\"\r\n" {
+ exp_continue
+ }
+ -re "^\\^done\r\n" {
+ exp_continue
+ }
+ -re "^$::mi_gdb_prompt$" {
+ }
+ }
+
+ if { $dyld_start_addr eq "MULTIPLE" } {
+ set dyld_start_addr ""
+ }
+
+ return [list $dyld_count $dyld_start_addr]
+}
+
+# Run the inferior over all the 'dlclose' calls and capture the
+# resulting library-unloaded events. Check that we see the expected
+# number of unload events for the libraries created for this test, and
+# additionally, check for dynamic linker unload events.
+proc check_solib_unload_events {} {
+ mi_clean_restart $::binfile
+
+ if {[mi_runto_main] == -1} {
+ return
+ }
+
+ # After starting we expect the dynamic linker to be loaded exactly
+ # once. If it is not then we'll not be able to check the dynamic
+ # linker unloaded events later in this script.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ if { $dyld_count != 1 } {
+ unsupported "dynamic linker doesn't appear to be loaded"
+ return
+ }
+
+ # Create breakpoints.
+ mi_create_breakpoint "$::srcfile:$::bp_loaded" \
+ "create b/p once libraries are loaded" \
+ -disp keep -func main -file ".*$::srcfile" -line $::bp_loaded
+ mi_create_breakpoint "$::srcfile:$::bp_main" "create b/p at dlclose" \
+ -disp keep -func main -file ".*$::srcfile" -line $::bp_main
+
+ # Run past all the dlopen and dlmopen calls.
+ mi_execute_to "exec-continue" "breakpoint-hit" main "" ".*" $::bp_loaded \
+ {"" "disp=\"keep\""} "continue until all libraries are loaded"
+
+ # Check that the dynamic linker has now been loaded multiple times.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ if { $dyld_count < 2 } {
+ unsupported "not enough instances of the dynamic linker are mapped in"
+ return
+ }
+
+ # Continue. This will run until the end of 'main', and will pass
+ # over all the dlclose calls.
+ if {[mi_send_resuming_command "exec-continue" "exec-next"] == -1} {
+ return
+ }
+
+ # As a result of all the dlclose calls we should see some library
+ # unload events. Process them now.
+ set dyld_unload_count 0
+ array set unload_counts {}
+ gdb_test_multiple "" "" -prompt $::mi_gdb_prompt {
+ -re "=library-unloaded,id=\"(\[^\"\]+)\",\[^\r\n\]+\r\n" {
+ set lib $expect_out(1,string)
+ if {[is_dyln $lib]} {
+ # This is the dynamic linker being unloaded.
+ incr dyld_unload_count
+ }
+ set filename [file tail $lib]
+ incr unload_counts($filename)
+ exp_continue
+ }
+ -re "\\*stopped,reason=\"breakpoint-hit\",\[^\r\n\]+\r\n$::mi_gdb_prompt" {
+ }
+ }
+
+ # Check we saw the dynamic linker being unloaded the expected number of
+ # times.
+ gdb_assert { $dyld_unload_count == $dyld_count - 1 } \
+ "expected number of dynamic linker unloads"
+
+ # Check that we saw the expected number of library-unloaded events for
+ # each library. Each DESC is a list of two elements, a filename for a
+ # library, and the number of times it should have been unloaded.
+ foreach desc [list [list $::binfile_lib1 3] \
+ [list $::binfile_lib_dep 3] \
+ [list $::binfile_lib2 1]] {
+ set filename [file tail [lindex $desc 0]]
+ set count [lindex $desc 1]
+ gdb_assert { $unload_counts($filename) == $count } \
+ "check unload count for $filename"
+ }
+
+ # Check that the dynamic linker still shows as loaded exactly once.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ gdb_assert { $dyld_count == 1 } \
+ "dynamic linker is mapped once at final b/p"
+}
+
+check_solib_unload_events
return [expr $uid == 0]
}
+# Return nul-terminated string read from section SECTION of EXEC. Return ""
+# if no such section or nul-terminated string was found. Function is useful
+# for sections ".interp" or ".gnu_debuglink".
+
+proc section_get {exec section} {
+ global subdir
+ set tmp [standard_output_file section_get.tmp]
+ set objcopy_program [gdb_find_objcopy]
+
+ set command "exec $objcopy_program -O binary --set-section-flags $section=A --change-section-address $section=0 -j $section $exec $tmp"
+ verbose -log "command is $command"
+ set result [catch $command output]
+ verbose -log "result is $result"
+ verbose -log "output is $output"
+ if {$result == 1} {
+ return ""
+ }
+ set fi [open $tmp]
+ fconfigure $fi -translation binary
+ set data [read $fi]
+ close $fi
+ file delete $tmp
+ # .interp has size $len + 1 but .gnu_debuglink contains garbage after \000.
+ set len [string first \000 $data]
+ if {$len < 0} {
+ verbose -log "section $section not found"
+ return ""
+ }
+ set retval [string range $data 0 [expr $len - 1]]
+ verbose -log "section $section is <$retval>"
+ return $retval
+}
+
# Always load compatibility stuff.
load_lib future.exp
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# Return nul-terminated string read from section SECTION of EXEC. Return ""
-# if no such section or nul-terminated string was found. Function is useful
-# for sections ".interp" or ".gnu_debuglink".
-
-proc section_get {exec section} {
- global subdir
- set tmp [standard_output_file section_get.tmp]
- set objcopy_program [gdb_find_objcopy]
-
- set command "exec $objcopy_program -O binary --set-section-flags $section=A --change-section-address $section=0 -j $section $exec $tmp"
- verbose -log "command is $command"
- set result [catch $command output]
- verbose -log "result is $result"
- verbose -log "output is $output"
- if {$result == 1} {
- return ""
- }
- set fi [open $tmp]
- fconfigure $fi -translation binary
- set data [read $fi]
- close $fi
- file delete $tmp
- # .interp has size $len + 1 but .gnu_debuglink contains garbage after \000.
- set len [string first \000 $data]
- if {$len < 0} {
- verbose -log "section $section not found"
- return ""
- }
- set retval [string range $data 0 [expr $len - 1]]
- verbose -log "section $section is <$retval>"
- return $retval
-}
-
# Resolve symlinks.
proc symlink_resolve {file} {