diff --git a/gdb/symfile.c b/gdb/symfile.c index d583960d430..0413f7f7ce1 100644 --- a/gdb/symfile.c +++ b/gdb/symfile.c @@ -806,7 +806,6 @@ init_entry_point_info (struct objfile *objfile) if (ei->entry_point_p) { CORE_ADDR entry_point = ei->entry_point; - int found; /* Make certain that the address points at real code, and not a function descriptor. */ @@ -818,7 +817,7 @@ init_entry_point_info (struct objfile *objfile) ei->entry_point = gdbarch_addr_bits_remove (objfile->arch (), entry_point); - found = 0; + bool found = false; for (obj_section &osect : objfile->sections ()) { struct bfd_section *sect = osect.the_bfd_section; @@ -829,13 +828,17 @@ init_entry_point_info (struct objfile *objfile) { ei->the_bfd_section_index = gdb_bfd_section_index (objfile->obfd.get (), sect); - found = 1; + found = true; break; } } + /* We store the section index so that the entry address can be + relocated when used. If the entry address is outside of any + section then we cannot relocate it. Just claim that there is no + entry address in this case. */ if (!found) - ei->the_bfd_section_index = SECT_OFF_TEXT (objfile); + ei->entry_point_p = false; } } diff --git a/gdb/testsuite/gdb.base/solib-bad-entry-addr-lib.s b/gdb/testsuite/gdb.base/solib-bad-entry-addr-lib.s new file mode 100644 index 00000000000..595a5c5e132 --- /dev/null +++ b/gdb/testsuite/gdb.base/solib-bad-entry-addr-lib.s @@ -0,0 +1,33 @@ +/* Copyright 2026 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 . */ + + .section .rodata + .globl lib_var + .type lib_var, @object +lib_var: + .long 42 + .size lib_var, .-lib_var + + /* This executable section exists solely to force the linker + to create an additional LOAD segment (with R+X permissions), + giving the library 3+ LOAD segments instead of the default 2. + + This matters because GDB's symfile_find_segment_sections only + runs for objects with exactly 1 or 2 segments. When it runs, + it sets sect_index_text from the first section in segment 1, + which masks the bug we are testing for, see the .exp file for + details. */ + .section .not_text, "ax", @progbits + .byte 0 diff --git a/gdb/testsuite/gdb.base/solib-bad-entry-addr.c b/gdb/testsuite/gdb.base/solib-bad-entry-addr.c new file mode 100644 index 00000000000..9c5c7b491e0 --- /dev/null +++ b/gdb/testsuite/gdb.base/solib-bad-entry-addr.c @@ -0,0 +1,22 @@ +/* Copyright 2026 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 . */ + +extern int lib_var; + +int +main () +{ + return lib_var; +} diff --git a/gdb/testsuite/gdb.base/solib-bad-entry-addr.exp b/gdb/testsuite/gdb.base/solib-bad-entry-addr.exp new file mode 100644 index 00000000000..39694bffe63 --- /dev/null +++ b/gdb/testsuite/gdb.base/solib-bad-entry-addr.exp @@ -0,0 +1,99 @@ +# Copyright 2026 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 . + +# This test aims to exercise a specific situation which was seen +# causing GDB to crash. An application has a shared library that +# meets the following conditions: +# +# 1. No ".text" section, +# 2. at least 3 LOAD segments, and +# 3. a non-zero entry address that is outside of every section. +# +# When these 3 conditions are met then GDB would run into a problem in +# init_entry_point_info for the shared library. The non-zero entry +# address means GDB would try to find the section corresponding to the +# address. As the address is outside of all sections then GDB would +# try to use the .text section as a fall-back. But there is no text +# section, so an internal error would be triggered. +# +# The 3 LOAD segments is important because, if there are only 1 or 2 +# segments GDB has a default in symfile_find_segment_sections where +# it assumes the text section is the first section in the first +# segment, which means init_entry_point_info will have a .text section +# to use. +# +# This test has a very simple assembler file (which hopefully contains +# no architecture specific content), which will compile to a couple of +# sections, but no .text section. These sections force the creation +# of more than 2 LOAD segments. The compiler flags then set the entry +# address to 0x1, which (we hope) is outside all sections. This +# assembler file is compiled into a shared library. + +require allow_shlib_tests +require !use_gdb_stub +require {istarget *-linux*} + +standard_testfile .c -lib.s + +# Build shared library. +set lib_testfile ${testfile}-lib.so +set lib_srcfile ${srcfile2} +set lib_binfile [standard_output_file ${lib_testfile}] +set lib_flags {shlib \ + additional_flags=-nostartfiles \ + additional_flags=-Wl,-e,0x1 \ + additional_flags=-Wl,-z,separate-code} +if { [build_executable "build solib" $lib_testfile $lib_srcfile \ + $lib_flags] == -1 } { + return +} + +# Build the test executable. +if { [build_executable "build exec" $testfile $srcfile \ + [list debug pie shlib=${lib_binfile}]] == -1 } { + return +} + +# Confirm we have more than 2 LOAD segments in the shared library. +set readelf_program [gdb_find_readelf] +set command "exec $readelf_program -Wl $lib_binfile" +verbose -log "command is $command" +set result [catch {{*}$command} output] +verbose -log "result is $result" +verbose -log "output is $output" +if {$result != 0} { + fail "read program headers from $lib_testfile" + return +} +if {![regexp {\nProgram Headers:\n *Type [^\n]* Align\n(.*?)\n\n} $output trash phdr]} { + fail "no Program Headers found" + return +} +set load_segment_count 0 +foreach line [regexp -line -all -inline {^ *LOAD .* 0x[0-9]+$} $phdr] { + incr load_segment_count +} +if { $load_segment_count <= 2 } { + fail "not enough LOAD segments" + return +} + +# Start GDB and run to main. If the shared library is causing issues +# then we will see an internal error once the inferior starts running. +clean_restart $testfile + +if {![runto_main message]} { + return 0 +}