mirror of
https://github.com/torvalds/linux.git
synced 2024-11-28 15:11:31 +00:00
arm64/scs: Deal with 64-bit relative offsets in FDE frames
In some cases, the compiler may decide to emit DWARF FDE frames with 64-bit signed fields for the code offset and range fields. This may happen when using the large code model, for instance, which permits an executable to be spread out over more than 4 GiB of address space. Whether this is the case can be inferred from the augmentation data in the CIE frame, so decode this data before processing the FDE frames. Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Sami Tolvanen <samitolvanen@google.com> Tested-by: Sami Tolvanen <samitolvanen@google.com> Link: https://lore.kernel.org/r/20241106185513.3096442-7-ardb+git@google.com Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
parent
ccf54058f5
commit
60de7a647f
@ -50,6 +50,10 @@ bool dynamic_scs_is_enabled;
|
||||
#define DW_CFA_GNU_negative_offset_extended 0x2f
|
||||
#define DW_CFA_hi_user 0x3f
|
||||
|
||||
#define DW_EH_PE_sdata4 0x0b
|
||||
#define DW_EH_PE_sdata8 0x0c
|
||||
#define DW_EH_PE_pcrel 0x10
|
||||
|
||||
enum {
|
||||
PACIASP = 0xd503233f,
|
||||
AUTIASP = 0xd50323bf,
|
||||
@ -125,6 +129,7 @@ struct eh_frame {
|
||||
u8 data_alignment_factor;
|
||||
u8 return_address_register;
|
||||
u8 augmentation_data_size;
|
||||
u8 fde_pointer_format;
|
||||
};
|
||||
|
||||
struct { // FDE
|
||||
@ -132,11 +137,18 @@ struct eh_frame {
|
||||
s32 range;
|
||||
u8 opcodes[];
|
||||
};
|
||||
|
||||
struct { // FDE
|
||||
s64 initial_loc64;
|
||||
s64 range64;
|
||||
u8 opcodes64[];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
static int scs_handle_fde_frame(const struct eh_frame *frame,
|
||||
int code_alignment_factor,
|
||||
bool use_sdata8,
|
||||
bool dry_run)
|
||||
{
|
||||
int size = frame->size - offsetof(struct eh_frame, opcodes) + 4;
|
||||
@ -144,6 +156,12 @@ static int scs_handle_fde_frame(const struct eh_frame *frame,
|
||||
const u8 *opcode = frame->opcodes;
|
||||
int l;
|
||||
|
||||
if (use_sdata8) {
|
||||
loc = (u64)&frame->initial_loc64 + frame->initial_loc64;
|
||||
opcode = frame->opcodes64;
|
||||
size -= 8;
|
||||
}
|
||||
|
||||
// assume single byte uleb128_t for augmentation data size
|
||||
if (*opcode & BIT(7))
|
||||
return EDYNSCS_INVALID_FDE_AUGM_DATA_SIZE;
|
||||
@ -210,6 +228,7 @@ static int scs_handle_fde_frame(const struct eh_frame *frame,
|
||||
int scs_patch(const u8 eh_frame[], int size)
|
||||
{
|
||||
int code_alignment_factor = 1;
|
||||
bool fde_use_sdata8 = false;
|
||||
const u8 *p = eh_frame;
|
||||
|
||||
while (size > 4) {
|
||||
@ -245,13 +264,24 @@ int scs_patch(const u8 eh_frame[], int size)
|
||||
return EDYNSCS_INVALID_CIE_HEADER;
|
||||
|
||||
code_alignment_factor = frame->code_alignment_factor;
|
||||
|
||||
switch (frame->fde_pointer_format) {
|
||||
case DW_EH_PE_pcrel | DW_EH_PE_sdata4:
|
||||
fde_use_sdata8 = false;
|
||||
break;
|
||||
case DW_EH_PE_pcrel | DW_EH_PE_sdata8:
|
||||
fde_use_sdata8 = true;
|
||||
break;
|
||||
default:
|
||||
return EDYNSCS_INVALID_CIE_SDATA_SIZE;
|
||||
}
|
||||
} else {
|
||||
ret = scs_handle_fde_frame(frame, code_alignment_factor,
|
||||
true);
|
||||
fde_use_sdata8, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
scs_handle_fde_frame(frame, code_alignment_factor,
|
||||
false);
|
||||
fde_use_sdata8, false);
|
||||
}
|
||||
|
||||
p += sizeof(frame->size) + frame->size;
|
||||
|
Loading…
Reference in New Issue
Block a user