Compare commits

...

3 Commits

Author SHA1 Message Date
Indu Bhagat
5ab84cb8c8 ld: generate SFrame stack trace info for .plt.got
PR/32298 sframe: no SFrame stack trace info generated for .plt.got

Add support to generate SFrame stack trace info for .plt.got section.
Enhance the current definition of struct elf_x86_sframe_plt to include
initialized SFrame FDE/FREs applicable for .plt.got section.  There are
two variants of .plt.got entries: 16 byte and 8 byte.

8 byte:
    ff 25 00 00 00 00     jmpq  *name@GOTPCREL(%rip)
    66 90                 xchg  %ax,%ax

16 byte:
    f3 0f 1e fa           endbr64
    ff 25 66 2f 00 00     jmpq  *name@GOTPCREL(%rip)
    66 0f 1f 44 00 00     nopw   0x0(%rax,%rax,1)

For the testcase, define some application symbols such that their PLT
entry is placed in .plt.got and ensure SFrame information is generated
with and without -z ibtplt.

ChangeLog:
	PR/32298
	* bfd/elf64-x86-64.c (elf_x86_64_link_setup_gnu_properties):
	PLT GOT entry size is different for IBT vs non IBT PLTs.
	* bfd/elfxx-x86.c (enum dynobj_sframe_plt_type): New enum for
	SFRAME_PLT_GOT.
	(_bfd_x86_elf_create_sframe_plt): Handle SFRAME_PLT_GOT.
	(_bfd_x86_elf_write_sframe_plt): Likewise.
	(_bfd_x86_elf_late_size_sections): Likewise.
	(_bfd_x86_elf_finish_dynamic_sections): Likewise.
	* bfd/elfxx-x86.h (struct elf_x86_sframe_plt): Add new members
	to keep information about PLT GOT entries.
	(struct elf_x86_link_hash_table): Add support for creating
	SFrame section for .plt.got.
	* ld/testsuite/ld-x86-64/x86-64.exp: Add new tests.
	* ld/testsuite/ld-x86-64/sframe-pltgot-1.d: New test.
	* ld/testsuite/ld-x86-64/sframe-pltgot-1.s: New test.
	* ld/testsuite/ld-x86-64/sframe-pltgot-2.d: New test.
2024-10-30 08:42:25 -07:00
Josh Poimboeuf
a36d6ffd9b ld: fix wrong SFrame info for lazy IBT PLT
Fix PR/32296 sframe: wrong SFrame info for pltN and .plt.sec for -z ibtplt

The x86 psABI defines a 2-PLT scheme for IBT which uses .plt and
.plt.sec entries.  It was observed that SFrame information for .plt.sec
section was incorrect.  The erroneous assumption was that SFrame stack
trace information for .plt.sec with lazy binding is the same as SFrame
stack trace information for .plt with lazy binding.  This is corrected
now by initializing a new SFrame PLT helper object
elf_x86_64_sframe_ibt_plt for lazy PLT with IBT.

Add a testcase where linking with -z ibtplt generates .plt.sec entries and
ensure correct SFrame information for it.

ChangeLog:
	PR/32296
	* bfd/elf64-x86-64.c (elf_x86_64_sframe_ibt_pltn_fre2): New
	definition elf_x86_64_sframe_ibt_plt.  Use it in
	elf_x86_64_sframe_plt.
	(elf_x86_64_link_setup_gnu_properties): Lazy IBT PLT entries are
	different from lazy PLT.
        * bfd/elfxx-x86.c (_bfd_x86_elf_create_sframe_plt): Adjust for
	SFrame for IBT PLT.
        * ld/testsuite/ld-x86-64/x86-64.exp: Add new test.
        * ld/testsuite/ld-x86-64/sframe-ibt-plt-1.d: New test.
2024-10-30 08:41:32 -07:00
Josh Poimboeuf
395515de82 ld: fix PR/32297
When _creating_ SFrame information for the linker created .plt.sec, the
code correctly checks for presence of .plt.sec.  When _writing_ the
SFrame section for the corresponding .plt.sec, however, the conditionals
were wrongly checking for splt.  This was causing an assertion at link
time.

This issue has been known to affect glibc build with SFrame enabled.

No testcase is added just yet.  A later commit ensures correct SFrame
stack trace information is created for .plt.got. A test case (where only
.plt and .plt.got are created) is added then.

PR/32297 sframe: bfd assertion with empty main on IBT enabled system

ChangeLog:
	PR/32297
	* bfd/elfxx-x86.c (_bfd_x86_elf_late_size_sections): Check for
	  plt_second member not for splt.
2024-10-30 08:41:32 -07:00
8 changed files with 278 additions and 28 deletions

View File

@@ -906,6 +906,14 @@ static const sframe_frame_row_entry elf_x86_64_sframe_pltn_fre2 =
SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_1B) /* FRE info. */
};
/* .sframe FRE covering the .plt section entry for IBT. */
static const sframe_frame_row_entry elf_x86_64_sframe_ibt_pltn_fre2 =
{
9, /* SFrame FRE start address. */
{16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 12 bytes. */
SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_1B) /* FRE info. */
};
/* .sframe FRE covering the second .plt section entry. */
static const sframe_frame_row_entry elf_x86_64_sframe_sec_pltn_fre1 =
{
@@ -914,7 +922,7 @@ static const sframe_frame_row_entry elf_x86_64_sframe_sec_pltn_fre1 =
SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_1B) /* FRE info. */
};
/* SFrame helper object for non-lazy PLT. Also used for IBT enabled PLT. */
/* SFrame helper object for non-lazy PLT. */
static const struct elf_x86_sframe_plt elf_x86_64_sframe_non_lazy_plt =
{
LAZY_PLT_ENTRY_SIZE,
@@ -927,10 +935,34 @@ static const struct elf_x86_sframe_plt elf_x86_64_sframe_non_lazy_plt =
{ &elf_x86_64_sframe_sec_pltn_fre1, &elf_x86_64_sframe_null_fre },
0,
0, /* There is no second PLT necessary. */
{ &elf_x86_64_sframe_null_fre }
{ &elf_x86_64_sframe_null_fre },
NON_LAZY_PLT_ENTRY_SIZE,
1, /* Number of FREs for PLT GOT. */
/* Array of SFrame FREs for PLT GOT. */
{ &elf_x86_64_sframe_null_fre },
};
/* SFrame helper object for lazy PLT. Also used for IBT enabled PLT. */
/* SFrame helper object for non-lazy IBT enabled PLT. */
static const struct elf_x86_sframe_plt elf_x86_64_sframe_non_lazy_ibt_plt =
{
LAZY_PLT_ENTRY_SIZE,
2, /* Number of FREs for PLT0. */
/* Array of SFrame FREs for plt0. */
{ &elf_x86_64_sframe_plt0_fre1, &elf_x86_64_sframe_plt0_fre2 },
LAZY_PLT_ENTRY_SIZE,
1, /* Number of FREs for PLTn. */
/* Array of SFrame FREs for plt. */
{ &elf_x86_64_sframe_sec_pltn_fre1, &elf_x86_64_sframe_null_fre },
0,
0, /* There is no second PLT necessary. */
{ &elf_x86_64_sframe_null_fre },
LAZY_PLT_ENTRY_SIZE,
1, /* Number of FREs for PLT GOT. */
/* Array of SFrame FREs for PLT GOT. */
{ &elf_x86_64_sframe_null_fre },
};
/* SFrame helper object for lazy PLT. */
static const struct elf_x86_sframe_plt elf_x86_64_sframe_plt =
{
LAZY_PLT_ENTRY_SIZE,
@@ -942,10 +974,34 @@ static const struct elf_x86_sframe_plt elf_x86_64_sframe_plt =
/* Array of SFrame FREs for plt. */
{ &elf_x86_64_sframe_pltn_fre1, &elf_x86_64_sframe_pltn_fre2 },
NON_LAZY_PLT_ENTRY_SIZE,
1, /* Number of FREs for PLTn for second PLT. */
/* FREs for second plt (stack trace info for .plt.got is
identical). Used when IBT or non-lazy PLT is in effect. */
{ &elf_x86_64_sframe_sec_pltn_fre1 }
1, /* Number of FREs for second PLT. */
/* Array of SFrame FREs for second PLT. */
{ &elf_x86_64_sframe_sec_pltn_fre1 },
NON_LAZY_PLT_ENTRY_SIZE,
1, /* Number of FREs for PLT GOT. */
/* Array of SFrame FREs for PLT GOT. */
{ &elf_x86_64_sframe_null_fre },
};
/* SFrame helper object for lazy PLT with IBT. */
static const struct elf_x86_sframe_plt elf_x86_64_sframe_ibt_plt =
{
LAZY_PLT_ENTRY_SIZE,
2, /* Number of FREs for PLT0. */
/* Array of SFrame FREs for plt0. */
{ &elf_x86_64_sframe_plt0_fre1, &elf_x86_64_sframe_plt0_fre2 },
LAZY_PLT_ENTRY_SIZE,
2, /* Number of FREs for PLTn. */
/* Array of SFrame FREs for plt. */
{ &elf_x86_64_sframe_pltn_fre1, &elf_x86_64_sframe_ibt_pltn_fre2 },
LAZY_PLT_ENTRY_SIZE,
1, /* Number of FREs for second PLT. */
/* Array of SFrame FREs for second plt. */
{ &elf_x86_64_sframe_sec_pltn_fre1 },
LAZY_PLT_ENTRY_SIZE,
1, /* Number of FREs for PLT GOT. */
/* Array of SFrame FREs for PLT GOT. */
{ &elf_x86_64_sframe_null_fre },
};
/* These are the standard parameters. */
@@ -5678,8 +5734,8 @@ elf_x86_64_link_setup_gnu_properties (struct bfd_link_info *info)
{
init_table.sframe_lazy_plt = &elf_x86_64_sframe_plt;
init_table.sframe_non_lazy_plt = &elf_x86_64_sframe_non_lazy_plt;
init_table.sframe_lazy_ibt_plt = &elf_x86_64_sframe_plt;
init_table.sframe_non_lazy_ibt_plt = &elf_x86_64_sframe_non_lazy_plt;
init_table.sframe_lazy_ibt_plt = &elf_x86_64_sframe_ibt_plt;
init_table.sframe_non_lazy_ibt_plt = &elf_x86_64_sframe_non_lazy_ibt_plt;
}
else
{

View File

@@ -1817,7 +1817,8 @@ elf_x86_relative_reloc_compare (const void *pa, const void *pb)
enum dynobj_sframe_plt_type
{
SFRAME_PLT = 1,
SFRAME_PLT_SEC = 2
SFRAME_PLT_SEC = 2,
SFRAME_PLT_GOT = 3,
};
/* Create SFrame stack trace info for the plt entries in the .plt section
@@ -1831,7 +1832,6 @@ _bfd_x86_elf_create_sframe_plt (bfd *output_bfd,
struct elf_x86_link_hash_table *htab;
const struct elf_backend_data *bed;
bool plt0_generated_p;
unsigned int plt0_entry_size;
unsigned char func_info;
uint32_t fre_type;
@@ -1845,14 +1845,11 @@ _bfd_x86_elf_create_sframe_plt (bfd *output_bfd,
unsigned plt_entry_size = 0;
unsigned int num_pltn_fres = 0;
unsigned int num_pltn_entries = 0;
const sframe_frame_row_entry * const *pltn_fres;
bed = get_elf_backend_data (output_bfd);
htab = elf_x86_hash_table (info, bed->target_id);
/* Whether SFrame stack trace info for plt0 is to be generated. */
plt0_generated_p = htab->plt.has_plt0;
plt0_entry_size
= (plt0_generated_p) ? htab->sframe_plt->plt0_entry_size : 0;
switch (plt_sec_type)
{
case SFRAME_PLT:
@@ -1860,7 +1857,10 @@ _bfd_x86_elf_create_sframe_plt (bfd *output_bfd,
ectx = &htab->plt_cfe_ctx;
dpltsec = htab->elf.splt;
plt_entry_size = htab->plt.plt_entry_size;
plt0_entry_size
= htab->plt.has_plt0 ? htab->sframe_plt->plt0_entry_size : 0;
plt_entry_size = htab->sframe_plt->pltn_entry_size;
pltn_fres = htab->sframe_plt->pltn_fres;
num_pltn_fres = htab->sframe_plt->pltn_num_fres;
num_pltn_entries
= (dpltsec->size - plt0_entry_size) / plt_entry_size;
@@ -1870,14 +1870,32 @@ _bfd_x86_elf_create_sframe_plt (bfd *output_bfd,
case SFRAME_PLT_SEC:
{
ectx = &htab->plt_second_cfe_ctx;
/* FIXME - this or htab->plt_second_sframe ? */
dpltsec = htab->plt_second_eh_frame;
dpltsec = htab->plt_second;
plt0_entry_size = 0;
plt_entry_size = htab->sframe_plt->sec_pltn_entry_size;
pltn_fres = htab->sframe_plt->sec_pltn_fres;
num_pltn_fres = htab->sframe_plt->sec_pltn_num_fres;
num_pltn_entries = dpltsec->size / plt_entry_size;
break;
}
case SFRAME_PLT_GOT:
{
ectx = &htab->plt_got_cfe_ctx;
dpltsec = htab->plt_got;
plt0_entry_size = 0;
plt_entry_size = htab->sframe_plt->plt_got_entry_size;
pltn_fres = htab->sframe_plt->plt_got_fres;
num_pltn_fres = htab->sframe_plt->plt_got_num_fres;
num_pltn_entries = dpltsec->size / plt_entry_size;
break;
}
default:
/* No other value is possible. */
return false;
@@ -1897,7 +1915,7 @@ _bfd_x86_elf_create_sframe_plt (bfd *output_bfd,
/* Add SFrame FDE and the associated FREs for plt0 if plt0 has been
generated. */
if (plt0_generated_p)
if (plt0_entry_size)
{
/* Add SFrame FDE for plt0, the function start address is updated later
at _bfd_elf_merge_section_sframe time. */
@@ -1934,16 +1952,17 @@ _bfd_x86_elf_create_sframe_plt (bfd *output_bfd,
plt0_entry_size, /* func start addr. */
dpltsec->size - plt0_entry_size,
func_info,
16,
plt_entry_size,
0 /* Num FREs. */);
sframe_frame_row_entry pltn_fre;
/* Now add the FREs for pltn. Simply adding the two FREs suffices due
/* Now add the FREs for pltn. Simply adding the FREs suffices due
to the usage of SFRAME_FDE_TYPE_PCMASK above. */
for (unsigned int j = 0; j < num_pltn_fres; j++)
{
pltn_fre = *(htab->sframe_plt->pltn_fres[j]);
sframe_encoder_add_fre (*ectx, 1, &pltn_fre);
unsigned int func_idx = plt0_entry_size ? 1 : 0;
pltn_fre = *(pltn_fres[j]);
sframe_encoder_add_fre (*ectx, func_idx, &pltn_fre);
}
}
@@ -1981,6 +2000,10 @@ _bfd_x86_elf_write_sframe_plt (bfd *output_bfd,
ectx = htab->plt_second_cfe_ctx;
sec = htab->plt_second_sframe;
break;
case SFRAME_PLT_GOT:
ectx = htab->plt_got_cfe_ctx;
sec = htab->plt_got_sframe;
break;
default:
/* No other value is possible. */
return false;
@@ -2508,7 +2531,18 @@ _bfd_x86_elf_late_size_sections (bfd *output_bfd,
htab->plt_sframe->size = sizeof (sframe_header) + 1;
}
/* FIXME - generate for .plt.got ? */
if (htab->plt_got_sframe != NULL
&& htab->plt_got != NULL
&& htab->plt_got->size != 0
&& !bfd_is_abs_section (htab->plt_got->output_section))
{
_bfd_x86_elf_create_sframe_plt (output_bfd, info, SFRAME_PLT_GOT);
/* FIXME - Dirty Hack. Set the size to something non-zero for now,
so that the section does not get stripped out below. The precise
size of this section is known only when the contents are
serialized in _bfd_x86_elf_write_sframe_plt. */
htab->plt_got_sframe->size = sizeof (sframe_header) + 1;
}
if (htab->plt_second_sframe != NULL
&& htab->plt_second != NULL
@@ -2575,6 +2609,7 @@ _bfd_x86_elf_late_size_sections (bfd *output_bfd,
|| s == htab->plt_second_eh_frame
|| s == htab->plt_sframe
|| s == htab->plt_second_sframe
|| s == htab->plt_got_sframe
|| s == htab->elf.sdynbss
|| s == htab->elf.sdynrelro)
{
@@ -2619,7 +2654,8 @@ _bfd_x86_elf_late_size_sections (bfd *output_bfd,
/* Skip allocating contents for .sframe section as it is written
out differently. See below. */
if ((s == htab->plt_sframe) || (s == htab->plt_second_sframe))
if ((s == htab->plt_sframe) || (s == htab->plt_second_sframe)
|| (s == htab->plt_got_sframe))
continue;
/* NB: Initially, the iplt section has minimal alignment to
@@ -2680,10 +2716,16 @@ _bfd_x86_elf_late_size_sections (bfd *output_bfd,
_bfd_x86_elf_write_sframe_plt (output_bfd, info, SFRAME_PLT);
if (htab->plt_second_sframe != NULL
&& htab->elf.splt != NULL
&& htab->elf.splt->size != 0
&& htab->plt_second != NULL
&& htab->plt_second->size != 0
&& htab->plt_second_sframe->contents == NULL)
_bfd_x86_elf_write_sframe_plt (output_bfd, info, SFRAME_PLT_SEC);
if (htab->plt_got_sframe != NULL
&& htab->plt_got != NULL
&& htab->plt_got->size != 0
&& htab->plt_got_sframe->contents == NULL)
_bfd_x86_elf_write_sframe_plt (output_bfd, info, SFRAME_PLT_GOT);
}
if (resolved_plt != NULL
@@ -2994,6 +3036,34 @@ _bfd_x86_elf_finish_dynamic_sections (bfd *output_bfd,
return NULL;
}
}
if (htab->plt_got_sframe != NULL
&& htab->plt_got_sframe->contents != NULL)
{
if (htab->plt_got != NULL
&& htab->plt_got->size != 0
&& (htab->plt_got->flags & SEC_EXCLUDE) == 0
&& htab->plt_got->output_section != NULL
&& htab->plt_got_sframe->output_section != NULL)
{
bfd_vma plt_start = htab->plt_got->output_section->vma;
bfd_vma sframe_start
= (htab->plt_got_sframe->output_section->vma
+ htab->plt_got_sframe->output_offset
+ PLT_SFRAME_FDE_START_OFFSET);
bfd_put_signed_32 (dynobj, plt_start - sframe_start,
htab->plt_got_sframe->contents
+ PLT_SFRAME_FDE_START_OFFSET);
}
if (htab->plt_got_sframe->sec_info_type == SEC_INFO_TYPE_SFRAME)
{
if (! _bfd_elf_merge_section_sframe (output_bfd, info,
htab->plt_got_sframe,
htab->plt_got_sframe->contents))
return NULL;
}
}
if (htab->elf.sgot && htab->elf.sgot->size > 0)
elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize
= htab->got_entry_size;
@@ -4761,7 +4831,18 @@ _bfd_x86_elf_link_setup_gnu_properties
htab->plt_second_sframe = sec;
}
/* FIXME - add later for plt_got. */
/* .plt.got. */
if (htab->plt_got != NULL)
{
sec = bfd_make_section_anyway_with_flags (dynobj,
".sframe",
flags);
if (sec == NULL)
info->callbacks->einfo (_("%F%P: failed to create PLT GOT .sframe section\n"));
htab->plt_got_sframe = sec;
}
}
}

View File

@@ -401,6 +401,10 @@ struct elf_x86_sframe_plt
unsigned int sec_pltn_entry_size;
unsigned int sec_pltn_num_fres;
const sframe_frame_row_entry *sec_pltn_fres[SFRAME_PLTN_MAX_NUM_FRES];
unsigned int plt_got_entry_size;
unsigned int plt_got_num_fres;
const sframe_frame_row_entry *plt_got_fres[SFRAME_PLTN_MAX_NUM_FRES];
};
struct elf_x86_lazy_plt_layout
@@ -606,6 +610,8 @@ struct elf_x86_link_hash_table
asection *plt_sframe;
sframe_encoder_ctx *plt_second_cfe_ctx;
asection *plt_second_sframe;
sframe_encoder_ctx *plt_got_cfe_ctx;
asection *plt_got_sframe;
/* Parameters describing PLT generation, lazy or non-lazy. */
struct elf_x86_plt_layout plt;

View File

@@ -0,0 +1,33 @@
#as: --gsframe
#source: ibt-plt-3.s
#objdump: --sframe=.sframe
#ld: -shared -z ibtplt --no-rosegment
#name: SFrame for IBT PLT .plt.sec
.*: +file format .*
Contents of the SFrame section .sframe:
Header :
Version: SFRAME_VERSION_2
Flags: SFRAME_F_FDE_SORTED
CFA fixed RA offset: \-8
#...
Function Index :
func idx \[0\]: pc = 0x1000, size = 16 bytes
STARTPC +CFA +FP +RA +
0+1000 +sp\+16 +u +f +
0+1006 +sp\+24 +u +f +
func idx \[1\]: pc = 0x1010, size = 32 bytes
STARTPC\[m\] +CFA +FP +RA +
0+0000 +sp\+8 +u +f +
0+0009 +sp\+16 +u +f +
func idx \[2\]: pc = 0x1030, size = 32 bytes
STARTPC\[m\] +CFA +FP +RA +
0+0000 +sp\+8 +u +f +
#...

View File

@@ -0,0 +1,28 @@
#as: --gsframe
#source: sframe-pltgot-1.s
#objdump: --sframe=.sframe
#ld: -shared -z ibtplt --no-rosegment
#name: SFrame for IBT .plt.got
.*: +file format .*
Contents of the SFrame section .sframe:
Header :
Version: SFRAME_VERSION_2
Flags: SFRAME_F_FDE_SORTED
CFA fixed RA offset: \-8
#...
Function Index :
func idx \[0\]: pc = 0x1000, size = 16 bytes
STARTPC +CFA +FP +RA +
0+1000 +sp\+16 +u +f +
0+1006 +sp\+24 +u +f +
func idx \[1\]: pc = 0x1010, size = 64 bytes
STARTPC\[m\] +CFA +FP +RA +
0+0000 +sp\+16 +u +f +
#...

View File

@@ -0,0 +1,15 @@
.text
.globl foo
.type foo, @function
foo:
.cfi_startproc
call func1@plt
movq func1@GOTPCREL(%rip), %rax
call func2@plt
movq func2@GOTPCREL(%rip), %rax
call func3@plt
movq func3@GOTPCREL(%rip), %rax
call func4@plt
movq func4@GOTPCREL(%rip), %rax
.cfi_endproc
.section .note.GNU-stack,"",@progbits

View File

@@ -0,0 +1,28 @@
#as: --gsframe
#source: sframe-pltgot-1.s
#objdump: --sframe=.sframe
#ld: -shared --no-rosegment
#name: SFrame for .plt.got
.*: +file format .*
Contents of the SFrame section .sframe:
Header :
Version: SFRAME_VERSION_2
Flags: SFRAME_F_FDE_SORTED
CFA fixed RA offset: \-8
#...
Function Index :
func idx \[0\]: pc = 0x1000, size = 16 bytes
STARTPC +CFA +FP +RA +
0+1000 +sp\+16 +u +f +
0+1006 +sp\+24 +u +f +
func idx \[1\]: pc = 0x1010, size = 32 bytes
STARTPC\[m\] +CFA +FP +RA +
0+0000 +sp\+16 +u +f +
#...

View File

@@ -547,6 +547,9 @@ run_dump_test "pr32191-x32"
if { ![skip_sframe_tests] } {
run_dump_test "sframe-simple-1"
run_dump_test "sframe-plt-1"
run_dump_test "sframe-ibt-plt-1"
run_dump_test "sframe-pltgot-1"
run_dump_test "sframe-pltgot-2"
}
if ![istarget "x86_64-*-linux*"] {