forked from Imagelibrary/binutils-gdb
This patch convenes a set of changes in bfd, gas, ld, libsframe towards
moving to the new encoding for the 'sfde_func_start_address' field in
SFrame FDE.
First, gas must now mark all SFrame sections with the new flag
SFRAME_F_FDE_FUNC_START_PCREL. gas was already emitting the field
in the said encoding.
* gas/gen-sframe.c (output_sframe_internal): Emit the flag
SFRAME_F_FDE_FUNC_START_PCREL.
Similarly for ld, adopt the new semantics of sfde_func_start_address
consistently. This means:
- When merging SFrame sections, check that all input SFrame sections
have the SFRAME_F_FDE_FUNC_START_PCREL flag set. If the check
fails, ld errors out.
- When merging SFrame sections, keep even the in-memory contents of
the FDE function start address (buffer passed to libsframe
sframe_encoder_write () for writing out) encoded in the new
semantics. While it is, in theory, possible that instead of doing this
change here, we adjust the value of sfde_func_start_address at the final
write (sframe_encoder_write) time. But latter is not favorable for
maintenanance and may be generally confusing for developers.
- When creating SFrame for PLT entries, emit flag
SFRAME_F_FDE_FUNC_START_PCREL.
include/
* sframe-api.h (SFRAME_F_LD_MUSTHAVE_FLAGS): New definition.
bfd/
* elf-sframe.c (_bfd_elf_merge_section_sframe): Check for flag
combinatation SFRAME_F_LD_MUSTHAVE_FLAGS set for all input and
output SFrame sections. If not, error out. Also, adopt the new
semantics of function start address encoding.
* bfd/elfxx-x86.c (_bfd_x86_elf_create_sframe_plt): Emit flag
SFRAME_F_FDE_FUNC_START_PCREL.
Next, for dumping SFrame sections, now that we are emitting the same
encoding in GAS, non-relocatable and relocatable SFrame links, it is the
time to set relocate to TRUE in debug_displays[].
binutils/
* dwarf.c (struct dwarf_section_display): Allow sframe sections
to now be relocated.
gas/testsuite/
* gas/cfi-sframe/cfi-sframe-aarch64-pac-ab-key-1.d: Update the
test. Relocatable SFrame sections now display non-zero value
(appropriate function start address).
Now, as the SFrame sections on-disk and in-memory use the new semantics of
sfde_func_start_address encoding (i.e., function start address is the
offset from the sfde_func_start_address field to the start PC), the
calculation to make it human readable (i.e., relatable to the addresses
in .text sections) needs adjustment.
libsframe/
* sframe-dump.c (dump_sframe_func_with_fres): Adjust the
function start address for dumping.
Now that both the emission of the new encoding, and the relocation of
sections before dumping them is in place, it is time to adjust the
testcases.
gas/testsuite/
* gas/cfi-sframe/cfi-sframe-aarch64-1.d: Update expected output
to include SFRAME_F_FDE_FUNC_START_PCREL instead of NONE.
* gas/cfi-sframe/cfi-sframe-aarch64-2.d: Likewise.
* gas/cfi-sframe/cfi-sframe-aarch64-3.d: Likewise.
* gas/cfi-sframe/cfi-sframe-aarch64-4.d: Likewise.
* gas/cfi-sframe/cfi-sframe-common-1.d: Likewise.
* gas/cfi-sframe/cfi-sframe-common-10.d: Likewise.
* gas/cfi-sframe/cfi-sframe-common-11.d: Likewise.
* gas/cfi-sframe/cfi-sframe-common-2.d: Likewise.
* gas/cfi-sframe/cfi-sframe-common-3.d: Likewise.
* gas/cfi-sframe/cfi-sframe-common-4.d: Likewise.
* gas/cfi-sframe/cfi-sframe-common-5.d: Likewise.
* gas/cfi-sframe/cfi-sframe-common-6.d: Likewise.
* gas/cfi-sframe/cfi-sframe-common-7.d: Likewise.
* gas/cfi-sframe/cfi-sframe-common-8.d: Likewise.
* gas/cfi-sframe/cfi-sframe-common-9.d: Likewise.
* gas/cfi-sframe/cfi-sframe-x86_64-1.d: Likewise.
* gas/cfi-sframe/cfi-sframe-x86_64-2.d: Likewise.
* gas/cfi-sframe/cfi-sframe-x86_64-empty-1.d: Likewise.
* gas/cfi-sframe/cfi-sframe-x86_64-empty-2.d: Likewise.
* gas/cfi-sframe/cfi-sframe-x86_64-empty-3.d: Likewise.
* gas/cfi-sframe/cfi-sframe-x86_64-empty-4.d: Likewise.
* gas/cfi-sframe/common-empty-1.d: Likewise.
* gas/cfi-sframe/common-empty-2.d: Likewise.
* gas/cfi-sframe/common-empty-3.d: Likewise.
* gas/scfi/x86_64/scfi-cfi-sections-1.d: Likewise.
* gas/scfi/x86_64/scfi-dyn-stack-1.d: Likewise.
ld/testsuite/
* ld-aarch64/sframe-simple-1.d: Update expected output to
include SFRAME_F_FDE_FUNC_START_PCREL.
* ld-x86-64/sframe-ibt-plt-1.d: Likewise.
* ld-x86-64/sframe-plt-1.d: Likewise.
* ld-x86-64/sframe-pltgot-1.d: Likewise.
* ld-x86-64/sframe-pltgot-2.d: Likewise.
* ld-x86-64/sframe-simple-1.d: Likewise.
Naturally, the change of semantics for 'SFrame FDE function start address'
has consequences on the implementation in libsframe. As per the new
semantics:
- Function start address in the SFrame FDE (sfde_func_start_address)
is an offset from the FDE function start address field to the start
PC of the associated function.
Note that, the libsframe library brings the SFrame section contents into
its own memory to create a sframe_decoder_ctx object via sframe_decode
(). Many internal and user-interfacing APIs then may use
sframe_decoder_ctx object to interact and fulfill the work.
In context of changing semantics for sfde_func_start_address, following
relevant examples may help understand the impact:
- sframe_find_fre () finds a the SFrame stack trace data (SFrame FRE)
given a lookup offset (offset of lookup_pc from the start of SFrame
section). Now that the sfde_func_start_address includes the
distance from the sfde_func_start_address field to the start of
SFrame section itself, the comparison checks of
sfde_func_start_address with the incoming lookup offset need
adjustment.
- Some internal functions (sframe_get_funcdesc_with_addr_internal ()
finds SFrame FDE by using binary seach comparing
sfde_func_start_address fields, etc.) need adjustments.
- sframe_encoder_write () sorts the SFrame FDEs before writing out
the SFrame data. Sorting of SFrame FDE via the internal function
sframe_sort_funcdesc() needs adjustments: the new encoding of
sfde_func_start_address means the distances are not from the same
anchor, so cannot be sorted directly.
This patch takes the approach of adding a new internal function:
- sframe_decoder_get_secrel_func_start_addr (): This function returns
the offset of the start PC of the function from the start of SFrame
section, i.e., it gives a section-relative offset.
As the sframe_decoder_get_secrel_func_start_addr () API needs the value
of the function index in the FDE list, another internal API needs
sframe_fre_check_range_p () adjustments too.
Sorting the FDEs (via sframe_sort_funcdesc ()) is done by first bringing
all offsets in sfde_func_start_address relative to start of SFrame
section, followed by sorting, and then readjusting the offsets accroding
to the new position in the FDE list.
libsframe/
* sframe.c (sframe_decoder_get_secrel_func_start_addr): New
static function.
(sframe_fre_check_range_p): Adjust the interface a bit.
(sframe_get_funcdesc_with_addr_internal): Use
sframe_decoder_get_secrel_func_start_addr () when comparing
sfde_func_start_address with user input offset.
(sframe_find_fre): Adopt the new semantics.
(sframe_sort_funcdesc): Likewise.
For the libsframe testsuite, use the new encoding for FDE func start
addr: distance between the FDE sfde_func_start_address field and the
start PC of the function itself.
Use SFRAME_F_FDE_FUNC_START_PCREL flag, though the sframe_encode ()
interface in libsframe applies no sanity checks for the encoding itself.
libsframe/testsuite/
* libsframe.find/findfre-1.c: Adjust to use the new
SFRAME_F_FDE_FUNC_START_PCREL specific encoding.
* libsframe.find/findfunc-1.c: Likewise.
* libsframe.find/plt-findfre-1.c: Likewise.
* libsframe/testsuite/libsframe.decode/DATA2: Update data file
due to usage of new SFRAME_F_FDE_FUNC_START_PCREL flag.
* libsframe/testsuite/libsframe.encode/encode-1.c: Use flag
SFRAME_F_FDE_FUNC_START_PCREL.
264 lines
9.2 KiB
C
264 lines
9.2 KiB
C
/* findfunc-1.c -- Test for sframe_get_funcdesc_with_addr in libsframe.
|
|
|
|
Copyright (C) 2023-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/>. */
|
|
|
|
/* sframe_get_funcdesc_with_addr is a core API in the libsframe library, which
|
|
is used to find an FDE given a PC. It is used by sframe_find_fre (). The
|
|
latter is the mainstay for an SFrame based stack tracer.
|
|
|
|
The tests in here stress the sframe_get_funcdesc_with_addr API via calls to
|
|
the sframe_find_fre (). */
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "sframe-api.h"
|
|
|
|
/* DejaGnu should not use gnulib's vsnprintf replacement here. */
|
|
#undef vsnprintf
|
|
#include <dejagnu.h>
|
|
|
|
static int
|
|
add_fde1 (sframe_encoder_ctx *encode, uint32_t start_pc_vaddr,
|
|
uint32_t sframe_vaddr, int idx, uint32_t *func_size)
|
|
{
|
|
/* A contiguous block containing 4 FREs. */
|
|
#define FDE1_NUM_FRES 4
|
|
sframe_frame_row_entry fres[FDE1_NUM_FRES]
|
|
= { {0x0, {0x1, 0, 0}, 0x3},
|
|
{0x1, {0x2, 0xf0, 0}, 0x5},
|
|
{0x10, {0x3, 0xf0, 0}, 0x4},
|
|
{0x38, {0x8, 0xf0, 0}, 0x5}
|
|
};
|
|
/* Function size in bytes. P.S. Must be a value greater than the
|
|
fre_start_addr of the last FRE above (0x38). */
|
|
*func_size = 0x40;
|
|
|
|
uint32_t offsetof_fde_in_sec
|
|
= sframe_encoder_get_offsetof_fde_start_addr (encode, idx, NULL);
|
|
int32_t func1_start_addr = (start_pc_vaddr
|
|
- (sframe_vaddr + offsetof_fde_in_sec));
|
|
unsigned char finfo = sframe_fde_create_func_info (SFRAME_FRE_TYPE_ADDR1,
|
|
SFRAME_FDE_TYPE_PCINC);
|
|
int err = sframe_encoder_add_funcdesc (encode, func1_start_addr, *func_size,
|
|
finfo, FDE1_NUM_FRES);
|
|
if (err == -1)
|
|
return err;
|
|
|
|
for (unsigned int i = 0; i < FDE1_NUM_FRES; i++)
|
|
if (sframe_encoder_add_fre (encode, idx, fres + i) == SFRAME_ERR)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
add_fde2 (sframe_encoder_ctx *encode, uint32_t start_pc_vaddr,
|
|
uint32_t sframe_vaddr, int idx, uint32_t *func_size)
|
|
{
|
|
/* A contiguous block containing 4 FREs. */
|
|
#define FDE2_NUM_FRES 4
|
|
sframe_frame_row_entry fres[FDE2_NUM_FRES]
|
|
= { {0x0, {0x10, 0, 0}, 0x3},
|
|
{0x10, {0x12, 0xf0, 0}, 0x5},
|
|
{0x14, {0x14, 0xf0, 0}, 0x4},
|
|
{0x20, {0x15, 0xf0, 0}, 0x5}
|
|
};
|
|
/* Function size in bytes. P.S. Must be a value greater than the
|
|
fre_start_addr of the last FRE above (0x20). */
|
|
*func_size = 0x60;
|
|
|
|
uint32_t offsetof_fde_in_sec
|
|
= sframe_encoder_get_offsetof_fde_start_addr (encode, idx, NULL);
|
|
int32_t func2_start_addr = (start_pc_vaddr
|
|
- (sframe_vaddr + offsetof_fde_in_sec));
|
|
unsigned char finfo = sframe_fde_create_func_info (SFRAME_FRE_TYPE_ADDR1,
|
|
SFRAME_FDE_TYPE_PCINC);
|
|
int err = sframe_encoder_add_funcdesc (encode, func2_start_addr, *func_size,
|
|
finfo, FDE2_NUM_FRES);
|
|
if (err == -1)
|
|
return err;
|
|
|
|
for (unsigned int i = 0; i < FDE2_NUM_FRES; i++)
|
|
if (sframe_encoder_add_fre (encode, idx, fres + i) == SFRAME_ERR)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
add_fde3 (sframe_encoder_ctx *encode, uint32_t start_pc_vaddr,
|
|
uint32_t sframe_vaddr, int idx, uint32_t *func_size)
|
|
{
|
|
/* A contiguous block containing 4 FREs. */
|
|
#define FDE3_NUM_FRES 4
|
|
sframe_frame_row_entry fres[FDE3_NUM_FRES]
|
|
= { {0x0, {0x16, 0, 0}, 0x3},
|
|
{0x1, {0x17, 0xf0, 0}, 0x5},
|
|
{0x10, {0x18, 0xf0, 0}, 0x4},
|
|
{0x38, {0x19, 0xf0, 0}, 0x5}
|
|
};
|
|
/* Function size in bytes. P.S. Must be a value greater than the
|
|
fre_start_addr of the last FRE above (0x38). */
|
|
*func_size = 0x40;
|
|
|
|
uint32_t offsetof_fde_in_sec
|
|
= sframe_encoder_get_offsetof_fde_start_addr (encode, idx, NULL);
|
|
int32_t func3_start_addr = (start_pc_vaddr
|
|
- (sframe_vaddr + offsetof_fde_in_sec));
|
|
unsigned char finfo = sframe_fde_create_func_info (SFRAME_FRE_TYPE_ADDR1,
|
|
SFRAME_FDE_TYPE_PCINC);
|
|
int err = sframe_encoder_add_funcdesc (encode, func3_start_addr, *func_size,
|
|
finfo, FDE3_NUM_FRES);
|
|
if (err == -1)
|
|
return err;
|
|
|
|
for (unsigned int i = 0; i < FDE3_NUM_FRES; i++)
|
|
if (sframe_encoder_add_fre (encode, idx, fres + i) == SFRAME_ERR)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
void test_text_findfre (uint32_t text_vaddr, uint32_t sframe_vaddr)
|
|
{
|
|
sframe_encoder_ctx *encode;
|
|
sframe_decoder_ctx *dctx;
|
|
sframe_frame_row_entry frep;
|
|
uint32_t func1_start_vaddr;
|
|
uint32_t func2_start_vaddr;
|
|
uint32_t func3_start_vaddr;
|
|
uint32_t func1_size = 0;
|
|
uint32_t func2_size = 0;
|
|
uint32_t func3_size = 0;
|
|
uint32_t fde_cnt = 0;
|
|
int32_t lookup_pc = 0;
|
|
char *sframe_buf;
|
|
size_t sf_size;
|
|
int err = 0;
|
|
|
|
#define TEST(name, cond) \
|
|
do \
|
|
{ \
|
|
if (cond) \
|
|
pass (name); \
|
|
else \
|
|
fail (name); \
|
|
} \
|
|
while (0)
|
|
|
|
encode = sframe_encode (SFRAME_VERSION,
|
|
SFRAME_F_FDE_FUNC_START_PCREL,
|
|
SFRAME_ABI_AMD64_ENDIAN_LITTLE,
|
|
SFRAME_CFA_FIXED_FP_INVALID,
|
|
-8, /* Fixed RA offset for AMD64. */
|
|
&err);
|
|
|
|
/* Add FDE at index 0. */
|
|
func1_start_vaddr = text_vaddr;
|
|
err = add_fde1 (encode, func1_start_vaddr, sframe_vaddr, 0, &func1_size);
|
|
TEST ("findfunc-1: Adding FDE1", err == 0);
|
|
|
|
/* Add FDE at index 1. */
|
|
func2_start_vaddr = func1_start_vaddr + func1_size + 0x10;
|
|
err = add_fde2 (encode, func2_start_vaddr, sframe_vaddr, 1, &func2_size);
|
|
TEST ("findfunc-1: Adding FDE2", err == 0);
|
|
|
|
/* Add FDE at index 2. */
|
|
func3_start_vaddr = func2_start_vaddr + func2_size + 0x10;
|
|
err = add_fde3 (encode, func3_start_vaddr, sframe_vaddr, 2, &func3_size);
|
|
TEST ("findfunc-1: Adding FDE3", err == 0);
|
|
|
|
fde_cnt = sframe_encoder_get_num_fidx (encode);
|
|
TEST ("findfunc-1: Test FDE count", fde_cnt == 3);
|
|
|
|
sframe_buf = sframe_encoder_write (encode, &sf_size, &err);
|
|
TEST ("findfunc-1: Encoder write", err == 0);
|
|
|
|
dctx = sframe_decode (sframe_buf, sf_size, &err);
|
|
TEST ("findfunc-1: Decoder setup", dctx != NULL);
|
|
|
|
/* Following negative tests check that libsframe APIs
|
|
(sframe_get_funcdesc_with_addr, sframe_find_fre) work
|
|
well for PCs not covered by the FDEs. */
|
|
|
|
/* Search with PC less than the first FDE's start addr. */
|
|
lookup_pc = func1_start_vaddr - 0x15 - sframe_vaddr;
|
|
err = sframe_find_fre (dctx, lookup_pc, &frep);
|
|
TEST ("findfunc-1: test-1: Find FRE for PC not in range",
|
|
err == SFRAME_ERR);
|
|
|
|
/* Search with a PC between func1's last PC and func2's first PC. */
|
|
lookup_pc = func1_start_vaddr + func1_size + 0x1 - sframe_vaddr,
|
|
err = sframe_find_fre (dctx, lookup_pc, &frep);
|
|
TEST ("findfunc-1: test-2: Find FRE for PC not in range",
|
|
err == SFRAME_ERR);
|
|
|
|
/* Search for a PC between func2's last PC and func3's first PC. */
|
|
lookup_pc = func2_start_vaddr + func2_size + 0x3 - sframe_vaddr;
|
|
err = sframe_find_fre (dctx, lookup_pc, &frep);
|
|
TEST ("findfunc-1: test-3: Find FRE for PC not in range",
|
|
err == SFRAME_ERR);
|
|
|
|
/* Search for a PC beyond the last func, i.e., > func3's last PC. */
|
|
lookup_pc = func3_start_vaddr + func3_size + 0x10 - sframe_vaddr;
|
|
err = sframe_find_fre (dctx, lookup_pc, &frep);
|
|
TEST ("findfunc-1: test-4: Find FRE for PC not in range",
|
|
err == SFRAME_ERR);
|
|
|
|
/* And some positive tests... */
|
|
|
|
/* Find an FRE for PC in FDE1. */
|
|
lookup_pc = func1_start_vaddr + 0x9 - sframe_vaddr;
|
|
err = sframe_find_fre (dctx, lookup_pc, &frep);
|
|
TEST ("findfunc-1: Find FRE in FDE1",
|
|
(err == 0 && sframe_fre_get_cfa_offset (dctx, &frep, &err) == 0x2));
|
|
|
|
/* Find an FRE for PC in FDE2. */
|
|
lookup_pc = func2_start_vaddr + 0x11 - sframe_vaddr;
|
|
err = sframe_find_fre (dctx, lookup_pc, &frep);
|
|
TEST ("findfunc-1: Find FRE in FDE2",
|
|
(err == 0 && sframe_fre_get_cfa_offset (dctx, &frep, &err) == 0x12));
|
|
|
|
/* Find an FRE for PC in FDE3. */
|
|
lookup_pc = func3_start_vaddr + 0x10 - sframe_vaddr;
|
|
err = sframe_find_fre (dctx, lookup_pc, &frep);
|
|
TEST ("findfunc-1: Find FRE in FDE3",
|
|
(err == 0 && sframe_fre_get_cfa_offset (dctx, &frep, &err) == 0x18));
|
|
|
|
sframe_encoder_free (&encode);
|
|
sframe_decoder_free (&dctx);
|
|
}
|
|
|
|
int main (void)
|
|
{
|
|
uint32_t sframe_vaddr = 0x4b5620;
|
|
uint32_t text_vaddr = 0x4038b0;
|
|
printf ("Testing with text_vaddr = %#x; sframe_vaddr = %#x\n", text_vaddr,
|
|
sframe_vaddr);
|
|
test_text_findfre (text_vaddr, sframe_vaddr);
|
|
|
|
sframe_vaddr = 0x4038b0;
|
|
text_vaddr = 0x4b5620;
|
|
printf ("Testing with text_vaddr = %#x; sframe_vaddr = %#x\n", text_vaddr,
|
|
sframe_vaddr);
|
|
test_text_findfre (text_vaddr, sframe_vaddr);
|
|
}
|