]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
arm64/scs: Deal with 64-bit relative offsets in FDE frames
authorArd Biesheuvel <ardb@kernel.org>
Wed, 6 Nov 2024 18:55:16 +0000 (19:55 +0100)
committerCatalin Marinas <catalin.marinas@arm.com>
Fri, 8 Nov 2024 16:37:55 +0000 (16:37 +0000)
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>
arch/arm64/kernel/pi/patch-scs.c

index cec8f0a52bbc761a6f01a650122bba950484fc8f..55d0cd64ef71e692ee7acd41f03937570023cf90 100644 (file)
@@ -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;