@@ -33,29 +33,204 @@ class ObJitMemoryManager : public llvm::RTDyldMemoryManager
3333 void operator =(const ObJitMemoryManager&);
3434public:
3535 explicit ObJitMemoryManager (ObJitAllocator &allocator)
36- : allocator_(allocator)
36+ : allocator_(allocator),
37+ code_section_addr_(nullptr ),
38+ gcc_except_tab_addr_(nullptr ),
39+ gcc_except_tab_size_(0 )
3740 {}
3841 virtual ~ObJitMemoryManager () {}
3942
40- // / Allocate a memory block of (at least) the given size suitable for
41- // / executable code. The SectionID is a unique identifier assigned by the JIT
42- // / engine, and optionally recorded by the memory manager to access a loaded
43- // / section.
4443 virtual uint8_t *allocateCodeSection (
4544 uintptr_t Size, unsigned Alignment, unsigned SectionID,
4645 llvm::StringRef SectionName)
4746 {
48- return reinterpret_cast <uint8_t *>(allocator_.alloc (JMT_RWE , Size, Alignment));
47+ uint8_t *ptr = reinterpret_cast <uint8_t *>(allocator_.alloc (JMT_RWE , Size, Alignment));
48+ #if defined(__APPLE__)
49+ // Track __text section address for .eh_frame pc_begin fixup.
50+ if (SectionName == " __text" ) {
51+ code_section_addr_ = ptr;
52+ }
53+ #endif
54+ return ptr;
4955 }
5056
51- // / Allocate a memory block of (at least) the given size suitable for data.
52- // / The SectionID is a unique identifier assigned by the JIT engine, and
53- // / optionally recorded by the memory manager to access a loaded section.
5457 virtual uint8_t *allocateDataSection (
5558 uintptr_t Size, unsigned Alignment, unsigned SectionID,
5659 llvm::StringRef SectionName, bool IsReadOnly){
57- return reinterpret_cast <uint8_t *>(allocator_.alloc (JMT_RO , Size, Alignment));
60+ uint8_t *ptr = reinterpret_cast <uint8_t *>(allocator_.alloc (JMT_RO , Size, Alignment));
61+ #if defined(__APPLE__)
62+ // Track __gcc_except_tab section address for .eh_frame LSDA fixup.
63+ // On macOS ARM64, RuntimeDyld doesn't properly relocate the LSDA pointer
64+ // in .eh_frame (ARM64_RELOC_SUBTRACTOR+UNSIGNED pair), so we fix it up
65+ // manually in registerEHFrames.
66+ if (SectionName == " __gcc_except_tab" ) {
67+ gcc_except_tab_addr_ = ptr;
68+ gcc_except_tab_size_ = Size;
69+ }
70+ #endif
71+ return ptr;
72+ }
73+
74+ virtual void registerEHFrames (uint8_t *Addr, uint64_t LoadAddr, size_t Size) {
75+ #if defined(__APPLE__)
76+ // Fix up LSDA pointers in .eh_frame before registration.
77+ // RuntimeDyld on MachO ARM64 does not correctly apply the
78+ // ARM64_RELOC_SUBTRACTOR + ARM64_RELOC_UNSIGNED relocation pair
79+ // within __eh_frame, leaving the LSDA pointer unrelocated and pointing
80+ // to garbage memory. We parse the CIE/FDE and patch the LSDA pointer
81+ // to correctly reference __gcc_except_tab.
82+ if (Size > 0 ) {
83+ fixupEHFrameRelocations (Addr, Size);
84+ }
85+ #endif
86+ llvm::RTDyldMemoryManager::registerEHFrames (Addr, LoadAddr, Size);
87+ // Reset for next module
88+ code_section_addr_ = nullptr ;
89+ gcc_except_tab_addr_ = nullptr ;
90+ gcc_except_tab_size_ = 0 ;
91+ }
92+
93+ virtual void deregisterEHFrames () {
94+ llvm::RTDyldMemoryManager::deregisterEHFrames ();
95+ }
96+
97+ private:
98+ #if defined(__APPLE__)
99+ // Read a ULEB128 value, advancing the pointer
100+ static uint64_t readULEB128 (const uint8_t **p) {
101+ uint64_t result = 0 ;
102+ unsigned shift = 0 ;
103+ uint8_t byte;
104+ do {
105+ byte = **p; (*p)++;
106+ result |= ((uint64_t )(byte & 0x7f )) << shift;
107+ shift += 7 ;
108+ } while (byte & 0x80 );
109+ return result;
110+ }
111+
112+ // Get byte size of an encoded pointer given the DWARF encoding
113+ static size_t getEncodedPtrSize (uint8_t enc) {
114+ if (enc == 0xFF ) return 0 ; // DW_EH_PE_omit
115+ switch (enc & 0x0F ) {
116+ case 0x00 : return sizeof (uintptr_t ); // DW_EH_PE_absptr
117+ case 0x02 : return 2 ; // DW_EH_PE_udata2
118+ case 0x03 : return 4 ; // DW_EH_PE_udata4
119+ case 0x04 : return 8 ; // DW_EH_PE_udata8
120+ case 0x09 : return 2 ; // DW_EH_PE_sdata2
121+ case 0x0A : return 4 ; // DW_EH_PE_sdata4 (note: some refs use 0x0B)
122+ case 0x0B : return 4 ; // DW_EH_PE_sdata4
123+ case 0x0C : return 8 ; // DW_EH_PE_sdata8
124+ default : return sizeof (uintptr_t );
125+ }
126+ }
127+
128+ // Fix up pc_begin and LSDA pointers in .eh_frame FDEs.
129+ // RuntimeDyld on MachO ARM64 does not correctly apply
130+ // ARM64_RELOC_SUBTRACTOR + ARM64_RELOC_UNSIGNED pairs in __eh_frame,
131+ // so both pc_begin (function start) and LSDA pointer end up wrong.
132+ void fixupEHFrameRelocations (uint8_t *ehFrame, size_t size) {
133+ const uint8_t *p = ehFrame;
134+ const uint8_t *end = ehFrame + size;
135+
136+ // --- Parse CIE ---
137+ if (p + 4 > end) return ;
138+ uint32_t cie_length = *(const uint32_t *)p; p += 4 ;
139+ if (cie_length == 0 || p + cie_length > end) return ;
140+ const uint8_t *cie_end = p + cie_length;
141+
142+ if (p + 4 > cie_end) return ;
143+ uint32_t cie_id = *(const uint32_t *)p; p += 4 ;
144+ if (cie_id != 0 ) return ; // not a CIE
145+
146+ if (p >= cie_end) return ;
147+ uint8_t version = *p++;
148+
149+ const char *aug_str = (const char *)p;
150+ while (p < cie_end && *p) p++;
151+ if (p >= cie_end) return ;
152+ p++; // skip null terminator
153+
154+ bool has_z = false , has_L = false , has_P = false , has_R = false ;
155+ for (const char *a = aug_str; *a; a++) {
156+ switch (*a) {
157+ case ' z' : has_z = true ; break ;
158+ case ' L' : has_L = true ; break ;
159+ case ' P' : has_P = true ; break ;
160+ case ' R' : has_R = true ; break ;
161+ }
162+ }
163+
164+ readULEB128 (&p); // code_alignment_factor
165+ { uint8_t b; do { b = *p++; } while (b & 0x80 ); } // data_alignment_factor (SLEB128)
166+ if (version >= 3 ) { readULEB128 (&p); } else { p++; } // return_address_register
167+
168+ uint8_t lsda_encoding = 0xFF ;
169+ uint8_t fde_encoding = 0x00 ;
170+
171+ if (has_z) {
172+ readULEB128 (&p); // augmentation_data_length
173+ for (const char *a = aug_str; *a; a++) {
174+ if (*a == ' z' ) continue ;
175+ if (*a == ' P' ) {
176+ uint8_t penc = *p++;
177+ p += getEncodedPtrSize (penc);
178+ } else if (*a == ' L' ) {
179+ lsda_encoding = *p++;
180+ } else if (*a == ' R' ) {
181+ fde_encoding = *p++;
182+ }
183+ }
184+ }
185+
186+ // --- Parse FDE(s) ---
187+ p = cie_end;
188+ while (p + 4 <= end) {
189+ uint32_t fde_length = *(const uint32_t *)p; p += 4 ;
190+ if (fde_length == 0 ) break ;
191+ if (p + fde_length > end) break ;
192+ const uint8_t *fde_end = p + fde_length;
193+
194+ uint32_t cie_ptr = *(const uint32_t *)p; p += 4 ;
195+ if (cie_ptr == 0 ) { p = fde_end; continue ; } // another CIE
196+
197+ // --- Fix pc_begin ---
198+ size_t fde_ptr_sz = getEncodedPtrSize (fde_encoding);
199+ bool fde_pcrel = ((fde_encoding & 0x70 ) == 0x10 );
200+ if (code_section_addr_ != nullptr && fde_pcrel) {
201+ uint8_t *pc_begin_field = const_cast <uint8_t *>(p);
202+ if (fde_ptr_sz == 8 ) {
203+ int64_t correct = (int64_t )code_section_addr_ - (int64_t )pc_begin_field;
204+ *(int64_t *)pc_begin_field = correct;
205+ } else if (fde_ptr_sz == 4 ) {
206+ int32_t correct = (int32_t )((int64_t )code_section_addr_ - (int64_t )pc_begin_field);
207+ *(int32_t *)pc_begin_field = correct;
208+ }
209+ }
210+ p += fde_ptr_sz; // skip pc_begin
211+ p += fde_ptr_sz; // skip pc_range (not relocated, leave as-is)
212+
213+ // --- Fix LSDA pointer ---
214+ if (has_z) {
215+ readULEB128 (&p); // augmentation_data_length
216+ if (has_L && lsda_encoding != 0xFF && gcc_except_tab_addr_ != nullptr ) {
217+ uint8_t *lsda_field = const_cast <uint8_t *>(p);
218+ size_t lsda_ptr_sz = getEncodedPtrSize (lsda_encoding);
219+ bool lsda_pcrel = ((lsda_encoding & 0x70 ) == 0x10 );
220+
221+ if (lsda_pcrel && lsda_ptr_sz == 8 ) {
222+ int64_t correct = (int64_t )gcc_except_tab_addr_ - (int64_t )lsda_field;
223+ *(int64_t *)lsda_field = correct;
224+ } else if (lsda_pcrel && lsda_ptr_sz == 4 ) {
225+ int32_t correct = (int32_t )((int64_t )gcc_except_tab_addr_ - (int64_t )lsda_field);
226+ *(int32_t *)lsda_field = correct;
227+ }
228+ }
229+ }
230+ p = fde_end;
231+ }
58232 }
233+ #endif
59234
60235 // / This method is called when object loading is complete and section page
61236 // / permissions can be applied. It is up to the memory manager implementation
@@ -99,6 +274,9 @@ class ObJitMemoryManager : public llvm::RTDyldMemoryManager
99274
100275private:
101276 ObJitAllocator &allocator_;
277+ uint8_t *code_section_addr_;
278+ uint8_t *gcc_except_tab_addr_;
279+ size_t gcc_except_tab_size_;
102280};
103281
104282} // core
0 commit comments