Re: [PATCH] Add missing null pointer check in get_sal_arch

Andrew Burgess <[email protected]> Fri, 31 Jul 2026 16:27:06 +0100
Newsgroups gmane.comp.gdb.patches
Message-ID <[email protected]>
Andrew Burgess <[email protected]> writes:

> Craig Blackmore <[email protected]> writes:
>
>> This fixes a GDB crash when trying to set a breakpoint on a function in
>> an ELF where there is both no .text section and the first section within
>> the ELF is not allocatable.
>
> This tells us WHAT happened, but not WHY.  We understand the input as
> you gave a description of the ELF, and you explained the end result, a
> crash.  But it would be really useful if you could fill in the middle
> bit.  Why does the objfile end up as NULL?
>
> When a fix is "add a NULL pointer check" my immediate question is:
> should the pointer even be NULL?  Maybe there's a better fix elsewhere
> in GDB which prevents the pointer from ever becoming NULL.  The goal of
> the "middle bit" that I asked for above is to convince the reviewers
> that NULL is a valid possibility and that a NULL check should be added.
>
> This commit from April seems like it might be in a similar area of GDB:
>
>   commit cd289df068e39683576f95907b5dd06ae3e4e254
>   Date:   Wed Apr 15 10:43:31 2026 +0100
>
>     gdb: don't use .text as default entry point section
>
> and might be worth a read.

I looked at this a bit more and `init_objfile_sect_indices` ends with
this code:

  for (i = 0; i < objfile->section_offsets.size (); i++)
    {
      if (objfile->section_offsets[i] != 0)
	{
	  break;
	}
    }
  if (i == objfile->section_offsets.size ())
    {
      if (objfile->sect_index_text == -1)
	objfile->sect_index_text = 0;
      if (objfile->sect_index_data == -1)
	objfile->sect_index_data = 0;
      if (objfile->sect_index_bss == -1)
	objfile->sect_index_bss = 0;
      if (objfile->sect_index_rodata == -1)
	objfile->sect_index_rodata = 0;
    }

With the idea being that if every section has a relocation offset of
zero then we can just point at any section.  That's fine as far as the
actual relocation offset is concerned, but sect_index_text is also used
to find an objfile, and in this case, we need to point to an actual
allocatable section.

Maybe we should rewrite the 'if (objfile->sect_index_text == -1)' case
so instead of always selecting index 0 we select the first allocatable
and executable section?  I had a go at this, see the patch below, and
your test case still passes.

I also wondered if we should be adding an assert to catch this
problematic case earlier on?  In
buildsym_compunit::finish_block_internal where we do:

      symbol->set_section_index (SECT_OFF_TEXT (m_objfile));

this seems to be the first point where we could spot the problem maybe
as this is where the offset to the wrong section is used for a symbol.
Maybe here, or close to here, we could have an assert that the symbol
has a valid objfile?  I haven't exactly figured this bit out, but could
be something to investigate.

Anyway, let me know what you think of this alternative approach.

Thanks,
Andrew

---

commit 12f7a855e97e6f4623607b2a503952ff8a7bb5c3
Author: Andrew Burgess <[email protected]>
Date:   Wed Jul 29 18:21:12 2026 +0100

    WIP: possible alternative

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 7df63856278..ca600a845e5 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -7764,7 +7764,7 @@ set_breakpoint_location_function (struct bp_location *loc)
 struct gdbarch *
 get_sal_arch (struct symtab_and_line sal)
 {
-  if (sal.section != nullptr && sal.section->objfile != nullptr)
+  if (sal.section != nullptr)
     return sal.section->objfile->arch ();
   if (sal.symtab != nullptr)
     return sal.symtab->compunit ().objfile ()->arch ();
diff --git a/gdb/symfile.c b/gdb/symfile.c
index 017f7a49d8d..ee1c40dada9 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -102,6 +102,8 @@ static int simple_overlay_update_1 (struct obj_section *);
 
 static void symfile_find_segment_sections (struct objfile *objfile);
 
+static int symfile_default_text_sect_index (objfile *objfile);
+
 /* Map from a BFD flavour to the corresponding sym_fns instance.  On
    gdb startup, each object file reader calls add_symtab_fns() to
    register information on each format it is prepared to read.  */
@@ -300,7 +302,7 @@ init_objfile_sect_indices (struct objfile *objfile)
   if (i == objfile->section_offsets.size ())
     {
       if (objfile->sect_index_text == -1)
-	objfile->sect_index_text = 0;
+	objfile->sect_index_text = symfile_default_text_sect_index (objfile);
       if (objfile->sect_index_data == -1)
 	objfile->sect_index_data = 0;
       if (objfile->sect_index_bss == -1)
@@ -3706,6 +3708,48 @@ symfile_find_segment_sections (struct objfile *objfile)
     }
 }
 
+/* Return the section index of a section in OBJFILE which can act as
+   the default text section.
+
+   This returns the first allocatable and executable section, or the
+   first allocatable section if no section is marked executable.
+
+   As an absolute fallback, 0 is returned.  */
+
+static int
+symfile_default_text_sect_index (objfile *objfile)
+{
+  gdb_assert (objfile->sect_index_text == -1);
+
+  bfd *abfd = objfile->obfd.get ();
+
+  int first_allocatable_section_index = -1;
+
+  for (asection *sect = abfd->sections; sect != nullptr; sect = sect->next)
+    {
+      /* Skip non-allocatable sections.  */
+      if ((bfd_section_flags (sect) & SEC_ALLOC) == 0)
+	continue;
+
+      /* Record the first allocatable section.  */
+      if (first_allocatable_section_index == -1)
+	first_allocatable_section_index = sect->index;
+
+      /* Return the first allocatable code section found.  */
+      if ((bfd_section_flags (sect) & SEC_CODE) == SEC_CODE)
+	return sect->index;
+    }
+
+  /* We didn't even find an allocatable section.  Return 0, but this
+     is likely going to cause issues if (somehow) there are any debug
+     symbols in OBJFILE as those symbols will end up with a NULL
+     objfile pointer.  */
+  if (first_allocatable_section_index == -1)
+    return 0;
+
+  return first_allocatable_section_index;
+}
+
 /* Listen for free_objfile events.  */
 
 static void