1/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2018 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25#ifndef __MACH_O_FIXUP_CHAINS__
26#define __MACH_O_FIXUP_CHAINS__ 7
27
28
29#include <stdint.h>
30
31
32//#define LC_DYLD_EXPORTS_TRIE 0x80000033 // used with linkedit_data_command
33//#define LC_DYLD_CHAINED_FIXUPS 0x80000034 // used with linkedit_data_command, payload is dyld_chained_fixups_header
34
35
36// header of the LC_DYLD_CHAINED_FIXUPS payload
37struct dyld_chained_fixups_header
38{
39 uint32_t fixups_version; // 0
40 uint32_t starts_offset; // offset of dyld_chained_starts_in_image in chain_data
41 uint32_t imports_offset; // offset of imports table in chain_data
42 uint32_t symbols_offset; // offset of symbol strings in chain_data
43 uint32_t imports_count; // number of imported symbol names
44 uint32_t imports_format; // DYLD_CHAINED_IMPORT*
45 uint32_t symbols_format; // 0 => uncompressed, 1 => zlib compressed
46};
47
48// This struct is embedded in LC_DYLD_CHAINED_FIXUPS payload
49struct dyld_chained_starts_in_image
50{
51 uint32_t seg_count;
52 uint32_t seg_info_offset[1]; // each entry is offset into this struct for that segment
53 // followed by pool of dyld_chain_starts_in_segment data
54};
55
56// This struct is embedded in dyld_chain_starts_in_image
57// and passed down to the kernel for page-in linking
58struct dyld_chained_starts_in_segment
59{
60 uint32_t size; // size of this (amount kernel needs to copy)
61 uint16_t page_size; // 0x1000 or 0x4000
62 uint16_t pointer_format; // DYLD_CHAINED_PTR_*
63 uint64_t segment_offset; // offset in memory to start of segment
64 uint32_t max_valid_pointer; // for 32-bit OS, any value beyond this is not a pointer
65 uint16_t page_count; // how many pages are in array
66 uint16_t page_start[1]; // each entry is offset in each page of first element in chain
67 // or DYLD_CHAINED_PTR_START_NONE if no fixups on page
68 // uint16_t chain_starts[1]; // some 32-bit formats may require multiple starts per page.
69 // for those, if high bit is set in page_starts[], then it
70 // is index into chain_starts[] which is a list of starts
71 // the last of which has the high bit set
72};
73
74enum {
75 DYLD_CHAINED_PTR_START_NONE = 0xFFFF, // used in page_start[] to denote a page with no fixups
76 DYLD_CHAINED_PTR_START_MULTI = 0x8000, // used in page_start[] to denote a page which has multiple starts
77 DYLD_CHAINED_PTR_START_LAST = 0x8000, // used in chain_starts[] to denote last start in list for page
78};
79
80// these values are set in the reserved1 field of the __chain_starts section
81enum {
82 DYLD_CHAINED_STARTS_USE_FILE_OFFSET = 0x1, // denotes chain starts linked with -fixup_chains_section
83 DYLD_CHAINED_STARTS_USE_VM_OFFSET = 0x2, // denotes chain starts linked with -fixup_chains_section_vm
84};
85
86// This struct is embedded in __TEXT,__chain_starts section in firmware
87struct dyld_chained_starts_offsets
88{
89 uint32_t pointer_format; // DYLD_CHAINED_PTR_32_FIRMWARE or DYLD_CHAINED_PTR_ARM64E_KERNEL
90 uint32_t starts_count; // number of starts in array
91 uint32_t chain_starts[1]; // array chain start offsets
92};
93
94
95// values for dyld_chained_starts_in_segment.pointer_format
96enum {
97 DYLD_CHAINED_PTR_ARM64E = 1, // stride 8, unauth target is vmaddr
98 DYLD_CHAINED_PTR_64 = 2, // target is vmaddr
99 DYLD_CHAINED_PTR_32 = 3, // target is vmaddr
100 DYLD_CHAINED_PTR_32_CACHE = 4,
101 DYLD_CHAINED_PTR_32_FIRMWARE = 5,
102 DYLD_CHAINED_PTR_64_OFFSET = 6, // target is vm offset
103 DYLD_CHAINED_PTR_ARM64E_OFFSET = 7, // old name
104 DYLD_CHAINED_PTR_ARM64E_KERNEL = 7, // stride 4, unauth target is vm offset
105 DYLD_CHAINED_PTR_64_KERNEL_CACHE = 8,
106 DYLD_CHAINED_PTR_ARM64E_USERLAND = 9, // stride 8, unauth target is vm offset
107 DYLD_CHAINED_PTR_ARM64E_FIRMWARE = 10, // stride 4, unauth target is vmaddr
108 DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE = 11, // stride 1, x86_64 kernel caches
109 DYLD_CHAINED_PTR_ARM64E_USERLAND24 = 12, // stride 8, unauth target is vm offset, 24-bit bind
110 DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE = 13, // stride 8, regular/auth targets both vm offsets. Only A keys supported
111 DYLD_CHAINED_PTR_ARM64E_SEGMENTED = 14, // stride 4, rebase offsets use segIndex and segOffset
112};
113
114
115// DYLD_CHAINED_PTR_ARM64E
116struct dyld_chained_ptr_arm64e_rebase
117{
118 uint64_t target : 43,
119 high8 : 8,
120 next : 11, // 4 or 8-byte stide
121 bind : 1, // == 0
122 auth : 1; // == 0
123};
124
125// DYLD_CHAINED_PTR_ARM64E
126struct dyld_chained_ptr_arm64e_bind
127{
128 uint64_t ordinal : 16,
129 zero : 16,
130 addend : 19, // +/-256K
131 next : 11, // 4 or 8-byte stide
132 bind : 1, // == 1
133 auth : 1; // == 0
134};
135
136// DYLD_CHAINED_PTR_ARM64E
137struct dyld_chained_ptr_arm64e_auth_rebase
138{
139 uint64_t target : 32, // runtimeOffset
140 diversity : 16,
141 addrDiv : 1,
142 key : 2,
143 next : 11, // 4 or 8-byte stide
144 bind : 1, // == 0
145 auth : 1; // == 1
146};
147
148// DYLD_CHAINED_PTR_ARM64E
149struct dyld_chained_ptr_arm64e_auth_bind
150{
151 uint64_t ordinal : 16,
152 zero : 16,
153 diversity : 16,
154 addrDiv : 1,
155 key : 2,
156 next : 11, // 4 or 8-byte stide
157 bind : 1, // == 1
158 auth : 1; // == 1
159};
160
161// DYLD_CHAINED_PTR_64/DYLD_CHAINED_PTR_64_OFFSET
162struct dyld_chained_ptr_64_rebase
163{
164 uint64_t target : 36, // 64GB max image size (DYLD_CHAINED_PTR_64 => vmAddr, DYLD_CHAINED_PTR_64_OFFSET => runtimeOffset)
165 high8 : 8, // top 8 bits set to this (DYLD_CHAINED_PTR_64 => after slide added, DYLD_CHAINED_PTR_64_OFFSET => before slide added)
166 reserved : 7, // all zeros
167 next : 12, // 4-byte stride
168 bind : 1; // == 0
169};
170
171
172// DYLD_CHAINED_PTR_ARM64E_USERLAND24
173struct dyld_chained_ptr_arm64e_bind24
174{
175 uint64_t ordinal : 24,
176 zero : 8,
177 addend : 19, // +/-256K
178 next : 11, // 8-byte stide
179 bind : 1, // == 1
180 auth : 1; // == 0
181};
182
183// DYLD_CHAINED_PTR_ARM64E_USERLAND24
184struct dyld_chained_ptr_arm64e_auth_bind24
185{
186 uint64_t ordinal : 24,
187 zero : 8,
188 diversity : 16,
189 addrDiv : 1,
190 key : 2,
191 next : 11, // 8-byte stide
192 bind : 1, // == 1
193 auth : 1; // == 1
194};
195
196// DYLD_CHAINED_PTR_ARM64E_SEGMENTED
197struct dyld_chained_ptr_arm64e_segmented_rebase
198{
199 uint32_t targetSegOffset : 28, // offset in segment
200 targetSegIndex : 4; // index into segment address table
201 uint32_t padding : 19,
202 next : 12, // 4-byte stide
203 auth : 1; // == 0
204};
205
206// DYLD_CHAINED_PTR_ARM64E_SEGMENTED
207struct dyld_chained_ptr_arm64e_auth_segmented_rebase
208{
209 uint32_t targetSegOffset : 28, // offset in segment
210 targetSegIndex : 4; // index into segment address table
211 uint32_t diversity : 16,
212 addrDiv : 1,
213 key : 2,
214 next : 12, // 4-byte stide
215 auth : 1; // == 1
216};
217
218// DYLD_CHAINED_PTR_64
219struct dyld_chained_ptr_64_bind
220{
221 uint64_t ordinal : 24,
222 addend : 8, // 0 thru 255
223 reserved : 19, // all zeros
224 next : 12, // 4-byte stride
225 bind : 1; // == 1
226};
227
228// DYLD_CHAINED_PTR_64_KERNEL_CACHE, DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE
229struct dyld_chained_ptr_64_kernel_cache_rebase
230{
231 uint64_t target : 30, // basePointers[cacheLevel] + target
232 cacheLevel : 2, // what level of cache to bind to (indexes a mach_header array)
233 diversity : 16,
234 addrDiv : 1,
235 key : 2,
236 next : 12, // 1 or 4-byte stide
237 isAuth : 1; // 0 -> not authenticated. 1 -> authenticated
238};
239
240// DYLD_CHAINED_PTR_32
241// Note: for DYLD_CHAINED_PTR_32 some non-pointer values are co-opted into the chain
242// as out of range rebases. If an entry in the chain is > max_valid_pointer, then it
243// is not a pointer. To restore the value, subtract off the bias, which is
244// (64MB+max_valid_pointer)/2.
245struct dyld_chained_ptr_32_rebase
246{
247 uint32_t target : 26, // vmaddr, 64MB max image size
248 next : 5, // 4-byte stride
249 bind : 1; // == 0
250};
251
252// DYLD_CHAINED_PTR_32
253struct dyld_chained_ptr_32_bind
254{
255 uint32_t ordinal : 20,
256 addend : 6, // 0 thru 63
257 next : 5, // 4-byte stride
258 bind : 1; // == 1
259};
260
261// DYLD_CHAINED_PTR_32_CACHE
262struct dyld_chained_ptr_32_cache_rebase
263{
264 uint32_t target : 30, // 1GB max dyld cache TEXT and DATA
265 next : 2; // 4-byte stride
266};
267
268
269// DYLD_CHAINED_PTR_32_FIRMWARE
270struct dyld_chained_ptr_32_firmware_rebase
271{
272 uint32_t target : 26, // 64MB max firmware TEXT and DATA
273 next : 6; // 4-byte stride
274};
275
276// DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE
277struct dyld_chained_ptr_arm64e_shared_cache_rebase
278{
279 uint64_t runtimeOffset : 34, // offset from the start of the shared cache
280 high8 : 8,
281 unused : 10,
282 next : 11, // 8-byte stide
283 auth : 1; // == 0
284};
285
286// DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE
287struct dyld_chained_ptr_arm64e_shared_cache_auth_rebase
288{
289 uint64_t runtimeOffset : 34, // offset from the start of the shared cache
290 diversity : 16,
291 addrDiv : 1,
292 keyIsData : 1, // implicitly always the 'A' key. 0 -> IA. 1 -> DA
293 next : 11, // 8-byte stide
294 auth : 1; // == 1
295};
296
297
298
299// values for dyld_chained_fixups_header.imports_format
300enum {
301 DYLD_CHAINED_IMPORT = 1,
302 DYLD_CHAINED_IMPORT_ADDEND = 2,
303 DYLD_CHAINED_IMPORT_ADDEND64 = 3,
304};
305
306// DYLD_CHAINED_IMPORT
307struct dyld_chained_import
308{
309 uint32_t lib_ordinal : 8, // -15 .. 240 (0xF1 .. 0xF0)
310 weak_import : 1,
311 name_offset : 23;
312};
313
314// DYLD_CHAINED_IMPORT_ADDEND
315struct dyld_chained_import_addend
316{
317 uint32_t lib_ordinal : 8, // -15 .. 240 (0xF1 .. 0xF0)
318 weak_import : 1,
319 name_offset : 23;
320 int32_t addend;
321};
322
323// DYLD_CHAINED_IMPORT_ADDEND64
324struct dyld_chained_import_addend64
325{
326 uint64_t lib_ordinal : 16, // -15 .. 65520 (0xFFF1 .. 0xFFF0)
327 weak_import : 1,
328 reserved : 15,
329 name_offset : 32;
330 uint64_t addend;
331};
332
333#endif // __MACH_O_FIXUP_CHAINS__
334