PR30155, ld segfault in _bfd_nearby_section

The segfault was a symptom of messing with the absolute section next
field, confusing bfd_section_removed_from_list in linker.c:fix_syms.
That's not all that was going wrong.  The INSERT list of output
sections was being inserted into itself, ie. lost from the main
list of linker statements.

	PR 30155
	* ldlang.c (process_insert_statements): Handle pathological
	case of the insert script being inserted before the first
	output section statement in the default script.
	(output_prev_sec_find): Don't test section owner here.
	(insert_os_after): Change parameter to a list union pointer.
	(lang_insert_orphan): Test section owner here and adjust
	insert_os_after call.
This commit is contained in:
Alan Modra
2023-02-23 18:23:12 +10:30
parent 50980ba351
commit 18e7a6587e

View File

@ -1774,7 +1774,7 @@ output_prev_sec_find (lang_output_section_statement_type *os)
if (lookup->constraint < 0) if (lookup->constraint < 0)
continue; continue;
if (lookup->bfd_section != NULL && lookup->bfd_section->owner != NULL) if (lookup->bfd_section != NULL)
return lookup->bfd_section; return lookup->bfd_section;
} }
@ -1793,13 +1793,13 @@ output_prev_sec_find (lang_output_section_statement_type *os)
image symbols. */ image symbols. */
static lang_statement_union_type ** static lang_statement_union_type **
insert_os_after (lang_output_section_statement_type *after) insert_os_after (lang_statement_union_type *after)
{ {
lang_statement_union_type **where; lang_statement_union_type **where;
lang_statement_union_type **assign = NULL; lang_statement_union_type **assign = NULL;
bool ignore_first; bool ignore_first;
ignore_first = after == (void *) lang_os_list.head; ignore_first = after == lang_os_list.head;
for (where = &after->header.next; for (where = &after->header.next;
*where != NULL; *where != NULL;
@ -1936,7 +1936,9 @@ lang_insert_orphan (asection *s,
if (bfd_section == NULL) if (bfd_section == NULL)
bfd_section = output_prev_sec_find (after); bfd_section = output_prev_sec_find (after);
if (bfd_section != NULL && bfd_section != snew) if (bfd_section != NULL
&& bfd_section->owner != NULL
&& bfd_section != snew)
place->section = &bfd_section->next; place->section = &bfd_section->next;
} }
@ -2159,8 +2161,9 @@ lang_insert_orphan (asection *s,
/* Place OS after AFTER if AFTER_NOTE is TRUE. */ /* Place OS after AFTER if AFTER_NOTE is TRUE. */
if (place_after) if (place_after)
{ {
lang_statement_union_type **where = insert_os_after (after); lang_statement_union_type **where;
where = insert_os_after ((lang_statement_union_type *) after);
*add.tail = *where; *add.tail = *where;
*where = add.head; *where = add.head;
@ -4370,21 +4373,55 @@ process_insert_statements (lang_statement_union_type **start)
else else
link_info.output_bfd->section_last = first_sec->prev; link_info.output_bfd->section_last = first_sec->prev;
/* Add back. */ /* Add back. */
if (sec->owner == NULL)
/* SEC is the absolute section, from the
first dummy output section statement. Add
back the sections we trimmed off to the
start of the bfd sections. */
sec = NULL;
if (sec != NULL)
last_sec->next = sec->next; last_sec->next = sec->next;
if (sec->next != NULL) else
sec->next->prev = last_sec; last_sec->next = link_info.output_bfd->sections;
if (last_sec->next != NULL)
last_sec->next->prev = last_sec;
else else
link_info.output_bfd->section_last = last_sec; link_info.output_bfd->section_last = last_sec;
first_sec->prev = sec; first_sec->prev = sec;
sec->next = first_sec; if (first_sec->prev != NULL)
first_sec->prev->next = first_sec;
else
link_info.output_bfd->sections = first_sec;
}
} }
} }
first_os = NULL; lang_statement_union_type *after = (void *) where;
last_os = NULL; if (where == &lang_os_list.head->output_section_statement
&& where->next == first_os)
{
/* PR30155. Handle a corner case where the statement
list is something like the following:
. LOAD t.o
. .data 0x0000000000000000 0x0
. [0x0000000000000000] b = .
. *(.data)
. .data 0x0000000000000000 0x0 t.o
. 0x0000000000000000 0x4 LONG 0x0
. INSERT BEFORE .text.start
. [0x0000000000000004] a = .
. .text.start 0x0000000000000000 0x0
. [0x0000000000000000] c = .
. OUTPUT(a.out elf64-x86-64)
Here we do not want to allow insert_os_after to
choose a point inside the list we are moving.
That would lose the list. Instead, let
insert_os_after work from the INSERT, which in this
particular example will result in inserting after
the assignment "a = .". */
after = *s;
} }
ptr = insert_os_after (after);
ptr = insert_os_after (where);
/* Snip everything from the start of the list, up to and /* Snip everything from the start of the list, up to and
including the insert statement we are currently processing. */ including the insert statement we are currently processing. */
first = *start; first = *start;
@ -4395,6 +4432,8 @@ process_insert_statements (lang_statement_union_type **start)
statement_list.tail = s; statement_list.tail = s;
*ptr = first; *ptr = first;
s = start; s = start;
first_os = NULL;
last_os = NULL;
continue; continue;
} }
s = &(*s)->header.next; s = &(*s)->header.next;