Skip to content

Commit 60de7a6

Browse files
ardbiesheuvelctmarinas
authored andcommitted
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>
1 parent ccf5405 commit 60de7a6

File tree

1 file changed

+32
-2
lines changed

1 file changed

+32
-2
lines changed

arch/arm64/kernel/pi/patch-scs.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ bool dynamic_scs_is_enabled;
5050
#define DW_CFA_GNU_negative_offset_extended 0x2f
5151
#define DW_CFA_hi_user 0x3f
5252

53+
#define DW_EH_PE_sdata4 0x0b
54+
#define DW_EH_PE_sdata8 0x0c
55+
#define DW_EH_PE_pcrel 0x10
56+
5357
enum {
5458
PACIASP = 0xd503233f,
5559
AUTIASP = 0xd50323bf,
@@ -125,25 +129,39 @@ struct eh_frame {
125129
u8 data_alignment_factor;
126130
u8 return_address_register;
127131
u8 augmentation_data_size;
132+
u8 fde_pointer_format;
128133
};
129134

130135
struct { // FDE
131136
s32 initial_loc;
132137
s32 range;
133138
u8 opcodes[];
134139
};
140+
141+
struct { // FDE
142+
s64 initial_loc64;
143+
s64 range64;
144+
u8 opcodes64[];
145+
};
135146
};
136147
};
137148

138149
static int scs_handle_fde_frame(const struct eh_frame *frame,
139150
int code_alignment_factor,
151+
bool use_sdata8,
140152
bool dry_run)
141153
{
142154
int size = frame->size - offsetof(struct eh_frame, opcodes) + 4;
143155
u64 loc = (u64)offset_to_ptr(&frame->initial_loc);
144156
const u8 *opcode = frame->opcodes;
145157
int l;
146158

159+
if (use_sdata8) {
160+
loc = (u64)&frame->initial_loc64 + frame->initial_loc64;
161+
opcode = frame->opcodes64;
162+
size -= 8;
163+
}
164+
147165
// assume single byte uleb128_t for augmentation data size
148166
if (*opcode & BIT(7))
149167
return EDYNSCS_INVALID_FDE_AUGM_DATA_SIZE;
@@ -210,6 +228,7 @@ static int scs_handle_fde_frame(const struct eh_frame *frame,
210228
int scs_patch(const u8 eh_frame[], int size)
211229
{
212230
int code_alignment_factor = 1;
231+
bool fde_use_sdata8 = false;
213232
const u8 *p = eh_frame;
214233

215234
while (size > 4) {
@@ -245,13 +264,24 @@ int scs_patch(const u8 eh_frame[], int size)
245264
return EDYNSCS_INVALID_CIE_HEADER;
246265

247266
code_alignment_factor = frame->code_alignment_factor;
267+
268+
switch (frame->fde_pointer_format) {
269+
case DW_EH_PE_pcrel | DW_EH_PE_sdata4:
270+
fde_use_sdata8 = false;
271+
break;
272+
case DW_EH_PE_pcrel | DW_EH_PE_sdata8:
273+
fde_use_sdata8 = true;
274+
break;
275+
default:
276+
return EDYNSCS_INVALID_CIE_SDATA_SIZE;
277+
}
248278
} else {
249279
ret = scs_handle_fde_frame(frame, code_alignment_factor,
250-
true);
280+
fde_use_sdata8, true);
251281
if (ret)
252282
return ret;
253283
scs_handle_fde_frame(frame, code_alignment_factor,
254-
false);
284+
fde_use_sdata8, false);
255285
}
256286

257287
p += sizeof(frame->size) + frame->size;

0 commit comments

Comments
 (0)