@@ -421,10 +421,12 @@ enum {
421
421
DWRF_CFA_nop = 0x0 , // No operation
422
422
DWRF_CFA_offset_extended = 0x5 , // Extended offset instruction
423
423
DWRF_CFA_def_cfa = 0xc , // Define CFA rule
424
+ DWRF_CFA_def_cfa_register = 0xd , // Define CFA register
424
425
DWRF_CFA_def_cfa_offset = 0xe , // Define CFA offset
425
426
DWRF_CFA_offset_extended_sf = 0x11 , // Extended signed offset
426
427
DWRF_CFA_advance_loc = 0x40 , // Advance location counter
427
- DWRF_CFA_offset = 0x80 // Simple offset instruction
428
+ DWRF_CFA_offset = 0x80 , // Simple offset instruction
429
+ DWRF_CFA_restore = 0xc0 // Restore register
428
430
};
429
431
430
432
/* DWARF Exception Handling pointer encodings */
@@ -539,6 +541,7 @@ typedef struct ELFObjectContext {
539
541
uint8_t * p ; // Current write position in buffer
540
542
uint8_t * startp ; // Start of buffer (for offset calculations)
541
543
uint8_t * eh_frame_p ; // Start of EH frame data (for relative offsets)
544
+ uint8_t * fde_p ; // Start of FDE data (for PC-relative calculations)
542
545
uint32_t code_size ; // Size of the code being described
543
546
} ELFObjectContext ;
544
547
@@ -804,7 +807,7 @@ static void elf_init_ehframe(ELFObjectContext* ctx) {
804
807
*
805
808
* DWRF_SECTION(FDE,
806
809
* DWRF_U32((uint32_t)(p - framep)); // Offset to CIE (relative from here)
807
- * DWRF_U32(-0x30 ); // Initial PC-relative location of the code
810
+ * DWRF_U32(pc_relative_offset ); // PC-relative location of the code (calculated dynamically)
808
811
* DWRF_U32(ctx->code_size); // Code range covered by this FDE
809
812
* DWRF_U8(0); // Augmentation data length (none)
810
813
*
@@ -873,11 +876,15 @@ static void elf_init_ehframe(ELFObjectContext* ctx) {
873
876
*
874
877
* The FDE describes unwinding information specific to this function.
875
878
* It references the CIE and provides function-specific CFI instructions.
879
+ *
880
+ * The PC-relative offset is calculated after the entire EH frame is built
881
+ * to ensure accurate positioning relative to the synthesized DSO layout.
876
882
*/
877
883
DWRF_SECTION (FDE ,
878
884
DWRF_U32 ((uint32_t )(p - framep )); // Offset to CIE (backwards reference)
879
- DWRF_U32 (-0x30 ); // Machine code offset relative to .text
880
- DWRF_U32 (ctx -> code_size ); // Address range covered by this FDE (code lenght)
885
+ ctx -> fde_p = p ; // Remember where PC offset field is located for later calculation
886
+ DWRF_U32 (0 ); // Placeholder for PC-relative offset (calculated at end of elf_init_ehframe)
887
+ DWRF_U32 (ctx -> code_size ); // Address range covered by this FDE (code length)
881
888
DWRF_U8 (0 ); // Augmentation data length (none)
882
889
883
890
/*
@@ -888,17 +895,22 @@ static void elf_init_ehframe(ELFObjectContext* ctx) {
888
895
* conventions and register usage patterns.
889
896
*/
890
897
#ifdef __x86_64__
891
- /* x86_64 calling convention unwinding rules */
898
+ /* x86_64 calling convention unwinding rules with frame pointer */
892
899
# if defined(__CET__ ) && (__CET__ & 1 )
893
- DWRF_U8 (DWRF_CFA_advance_loc | 8 ); // Advance location by 8 bytes when CET protection is enabled
894
- # else
895
- DWRF_U8 (DWRF_CFA_advance_loc | 4 ); // Advance location by 4 bytes
900
+ DWRF_U8 (DWRF_CFA_advance_loc | 4 ); // Advance past endbr64 (4 bytes)
896
901
# endif
897
- DWRF_U8 (DWRF_CFA_def_cfa_offset ); // Redefine CFA offset
898
- DWRF_UV (16 ); // New offset: SP + 16
899
- DWRF_U8 (DWRF_CFA_advance_loc | 6 ); // Advance location by 6 bytes
900
- DWRF_U8 (DWRF_CFA_def_cfa_offset ); // Redefine CFA offset
901
- DWRF_UV (8 ); // New offset: SP + 8
902
+ DWRF_U8 (DWRF_CFA_advance_loc | 1 ); // Advance past push %rbp (1 byte)
903
+ DWRF_U8 (DWRF_CFA_def_cfa_offset ); // def_cfa_offset 16
904
+ DWRF_UV (16 );
905
+ DWRF_U8 (DWRF_CFA_offset | DWRF_REG_BP ); // offset r6 at cfa-16
906
+ DWRF_UV (2 );
907
+ DWRF_U8 (DWRF_CFA_advance_loc | 3 ); // Advance past mov %rsp,%rbp (3 bytes)
908
+ DWRF_U8 (DWRF_CFA_def_cfa_register ); // def_cfa_register r6
909
+ DWRF_UV (DWRF_REG_BP );
910
+ DWRF_U8 (DWRF_CFA_advance_loc | 3 ); // Advance past call *%rcx (2 bytes) + pop %rbp (1 byte) = 3
911
+ DWRF_U8 (DWRF_CFA_def_cfa ); // def_cfa r7 ofs 8
912
+ DWRF_UV (DWRF_REG_SP );
913
+ DWRF_UV (8 );
902
914
#elif defined(__aarch64__ ) && defined(__AARCH64EL__ ) && !defined(__ILP32__ )
903
915
/* AArch64 calling convention unwinding rules */
904
916
DWRF_U8 (DWRF_CFA_advance_loc | 1 ); // Advance location by 1 instruction (stp x29, x30)
@@ -922,6 +934,58 @@ static void elf_init_ehframe(ELFObjectContext* ctx) {
922
934
)
923
935
924
936
ctx -> p = p ; // Update context pointer to end of generated data
937
+
938
+ /* Calculate and update the PC-relative offset in the FDE
939
+ *
940
+ * When perf processes the jitdump, it creates a synthesized DSO with this layout:
941
+ *
942
+ * Synthesized DSO Memory Layout:
943
+ * ┌─────────────────────────────────────────────────────────────┐ < code_start
944
+ * │ Code Section │
945
+ * │ (round_up(code_size, 8) bytes) │
946
+ * ├─────────────────────────────────────────────────────────────┤ < start of EH frame data
947
+ * │ EH Frame Data │
948
+ * │ ┌─────────────────────────────────────────────────────┐ │
949
+ * │ │ CIE data │ │
950
+ * │ └─────────────────────────────────────────────────────┘ │
951
+ * │ ┌─────────────────────────────────────────────────────┐ │
952
+ * │ │ FDE Header: │ │
953
+ * │ │ - CIE offset (4 bytes) │ │
954
+ * │ │ - PC offset (4 bytes) <─ fde_offset_in_frame ─────┼────┼─> points to code_start
955
+ * │ │ - address range (4 bytes) │ │ (this specific field)
956
+ * │ │ CFI Instructions... │ │
957
+ * │ └─────────────────────────────────────────────────────┘ │
958
+ * ├─────────────────────────────────────────────────────────────┤ < reference_point
959
+ * │ EhFrameHeader │
960
+ * │ (navigation metadata) │
961
+ * └─────────────────────────────────────────────────────────────┘
962
+ *
963
+ * The PC offset field in the FDE must contain the distance from itself to code_start:
964
+ *
965
+ * distance = code_start - fde_pc_field
966
+ *
967
+ * Where:
968
+ * fde_pc_field_location = reference_point - eh_frame_size + fde_offset_in_frame
969
+ * code_start_location = reference_point - eh_frame_size - round_up(code_size, 8)
970
+ *
971
+ * Therefore:
972
+ * distance = code_start_location - fde_pc_field_location
973
+ * = (ref - eh_frame_size - rounded_code_size) - (ref - eh_frame_size + fde_offset_in_frame)
974
+ * = -rounded_code_size - fde_offset_in_frame
975
+ * = -(round_up(code_size, 8) + fde_offset_in_frame)
976
+ *
977
+ * Note: fde_offset_in_frame is the offset from EH frame start to the PC offset field,
978
+ *
979
+ */
980
+ if (ctx -> fde_p != NULL ) {
981
+ int32_t fde_offset_in_frame = (ctx -> fde_p - ctx -> startp );
982
+ int32_t rounded_code_size = round_up (ctx -> code_size , 8 );
983
+ int32_t pc_relative_offset = - (rounded_code_size + fde_offset_in_frame );
984
+
985
+
986
+ // Update the PC-relative offset in the FDE
987
+ * (int32_t * )ctx -> fde_p = pc_relative_offset ;
988
+ }
925
989
}
926
990
927
991
// =============================================================================
@@ -1117,6 +1181,7 @@ static void perf_map_jit_write_entry(void *state, const void *code_addr,
1117
1181
char buffer [1024 ]; // Buffer for DWARF data (1KB should be sufficient)
1118
1182
ctx .code_size = code_size ;
1119
1183
ctx .startp = ctx .p = (uint8_t * )buffer ;
1184
+ ctx .fde_p = NULL ; // Initialize to NULL, will be set when FDE is written
1120
1185
1121
1186
/* Generate EH frame (Exception Handling frame) data */
1122
1187
elf_init_ehframe (& ctx );
0 commit comments