1/*
2 * Copyright (c) 2021 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23#ifndef _MACH_O_UTILS_H_
24#define _MACH_O_UTILS_H_
25
26#include <stddef.h>
27#include <stdint.h>
28#include <stdbool.h>
29
30#include <mach-o/loader.h>
31#include <Availability.h>
32
33#include <TargetConditionals.h>
34
35
36#if __cplusplus
37extern "C" {
38#endif
39
40/*!
41 * @function macho_cpu_type_for_arch_name
42 *
43 * @abstract
44 * Converts an architecture name into a cpu type/subtype pair.
45 *
46 * @param archName
47 * An architecture name (e.g "arm64e" or "x86_64").
48 *
49 * @param type
50 * A pointer to where to store the cpu type of the given name.
51 *
52 * @param subtype
53 * A pointer to where to store the cpu subtype of the given name.
54 *
55 * @return
56 * If the archName is known, returns true and fills in the type/subtype.
57 * If the archName is unknown, returns false.
58 */
59extern bool macho_cpu_type_for_arch_name(const char* _Nonnull archName, cpu_type_t* _Nonnull type, cpu_subtype_t* _Nonnull subtype)
60__API_AVAILABLE(macos(13.0), ios(16.0), tvos(16.0), watchos(8.0));
61
62
63/*!
64 * @function macho_arch_name_for_cpu_type
65 *
66 * @abstract
67 * Converts a cpu type/subtype pair into the architecture name.
68 *
69 * @param type
70 * The cpu type from <machine/machine.h> (e.g CPU_TYPE_ARM64)
71 *
72 * @param subtype
73 * The cpu subtype from <machine/machine.h> (e.g CPU_SUBTYPE_ARM64E)
74 *
75 * @return
76 * Returns a static c-string which is the name for the cpu type/subtype (e.g. "arm64e").
77 * If the cpu type/subtype is unknown, NULL will be returned.
78 * The string returned is static and does not need to be deallocated.
79 */
80extern const char* _Nullable macho_arch_name_for_cpu_type(cpu_type_t type, cpu_subtype_t subtype)
81__API_AVAILABLE(macos(13.0), ios(16.0), tvos(16.0), watchos(8.0));
82
83
84/*!
85 * @function macho_arch_name_for_mach_header
86 *
87 * @abstract
88 * Returns the architecture name from the cpu type/subtype in a mach_header.
89 * This is a convenience wrapper around macho_arch_name_for_cpu_type().
90 *
91 * @param mh
92 * A pointer to the header of a mach-o file.
93 * If NULL is passed, the architecture name of the main executable will be returned.
94 *
95 * @return
96 * Returns a static c-string which is the name for architecture of the mach-o file (e.g. "arm64e").
97 * If the architecture is unknown, NULL will be returned.
98 * The string returned is static and does not need to be deallocated.
99 */
100extern const char* _Nullable macho_arch_name_for_mach_header(const struct mach_header* _Nullable mh)
101__API_AVAILABLE(macos(13.0), ios(16.0), tvos(16.0), watchos(8.0));
102
103#ifdef __BLOCKS__
104 #if __has_attribute(noescape)
105 #define __MACHO_NOESCAPE __attribute__((__noescape__))
106 #else
107 #define __MACHO_NOESCAPE
108 #endif
109
110/*!
111 * @function macho_for_each_slice
112 *
113 * @abstract
114 * Temporarily maps a mach-o or universal file and iterates the slices.
115 * If the file is mach-o, the block is called once with the mach-o file mapped.
116 * If the file is universal (aka fat), the block is called once per slice in the order in the header.
117 * If the path does not exist or does, but is not a mach-o file, the block is never called.
118 *
119 * @param path
120 * The path to the file to inspect.
121 *
122 * @param callback
123 * A block to call once per slice.
124 * Can be NULL. In which case the return value tells you if the file is mach-o or fat.
125 * The slice pointer is only valid during the block invocation.
126 * To stop iterating the slices, set *stop to true.
127 *
128 * @return
129 * Returns zero on success, otherwise it returns an errno value.
130 * Common returned errors:
131 * ENOENT - path does not exist
132 * EACCES - path exists put caller does not have permission to access it
133 * EFTYPE - path exists but it is not a mach-o or fat file
134 * EBADMACHO - path is a mach-o file, but it is malformed
135 */
136extern int macho_for_each_slice(const char* _Nonnull path, void (^ _Nullable callback)(const struct mach_header* _Nonnull slice, uint64_t sliceFileOffset, size_t size, bool* _Nonnull stop) __MACHO_NOESCAPE)
137__API_AVAILABLE(macos(13.0), ios(16.0), tvos(16.0), watchos(8.0));
138
139
140/*!
141 * @function macho_for_each_slice_in_fd
142 *
143 * @abstract
144 * Temporarily maps a mach-o or universal file and iterates the slices.
145 * If the fd is to a mach-o, the block is called once with the mach-o file mapped.
146 * If the fd is to a universal (aka fat), the block is called once per slice in the order in the header.
147 * If the fd is closed or not mmap()able, the block is never called.
148 *
149 * @param fd
150 * An open file descriptor to a mmap()able file.
151 *
152 * @param callback
153 * A block to call once per slice.
154 * Can be NULL. In which case the return value tells you if the file is mach-o or fat.
155 * The slice pointer is only valid during the block invocation.
156 * To stop iterating the slices, set *stop to true.
157 *
158 * @return
159 * Returns zero on success, otherwise it returns an errno value.
160 * Common returned errors:
161 * EFTYPE - fd content is not a mach-o or fat file
162 * EBADMACHO - fd content is a mach-o file, but it is malformed
163 */
164extern int macho_for_each_slice_in_fd(int fd, void (^ _Nullable callback)(const struct mach_header* _Nonnull slice, uint64_t sliceFileOffset, size_t size, bool* _Nonnull stop)__MACHO_NOESCAPE)
165__API_AVAILABLE(macos(13.0), ios(16.0), tvos(16.0), watchos(8.0));
166
167
168/*!
169 * @function macho_best_slice
170 *
171 * @abstract
172 * Examines a mach-o or universal file to find the slice that would be loaded. That is, for dylib/bundles, which
173 * slice dyld would load. For main executables, which slice the kernel would use.
174 * In simulator processes, only other simulator main executables will be considered loadable.
175 * If the file is mach-o and is the right arch and platform to load, the block is called once with the mach-o file mapped.
176 * If the file is universal (aka fat) file, the best slice is found and the block is called once with the mapped slice.
177 * If the file is universal (aka fat) file, but none of the slices are loadable, the callback is not called, and EBADARCH is returned.
178 * If the path does not exist or does but is not a mach-o or universal file, the block is never called, and an error is returned.
179 *
180 * @param path
181 * The path to the file to inspect.
182 *
183 * @param callback
184 * A block to call once with the best slice.
185 * Can be NULL. In which case the return value tells you if there was a loadable slice
186 * The slice pointer is only valid during the block invocation.
187 *
188 * @return
189 * Returns zero on success (meaning there is a best slice), otherwise it returns an errno value.
190 * Common returned errors:
191 * ENOENT - path does not exist
192 * EACCES - path exists put caller does not have permission to access it
193 * EFTYPE - path exists but it is not a mach-o or fat file
194 * EBADARCH - path exists and is mach-o or fat, but none of the slices are loadable
195 * EBADMACHO - path is a mach-o file, but it is malformed
196 */
197extern int macho_best_slice(const char* _Nonnull path, void (^ _Nullable bestSlice)(const struct mach_header* _Nonnull slice, uint64_t sliceFileOffset, size_t sliceSize)__MACHO_NOESCAPE)
198__API_AVAILABLE(macos(13.0), ios(16.0), tvos(16.0), watchos(8.0));
199
200
201/*!
202 * @function macho_best_slice_in_fd
203 *
204 * @abstract
205 * Examines a mach-o or universal file to find the slice that would be loaded. That is, for dylib/bundles, which
206 * slice dyld would load. For main executables, which slice the kernel would use.
207 * In simulator processes, only other simulator main executables will be considered loadable.
208 * If the fd is to a mach-o and is the right arch and platform to load, the block is call once with the mach-o file mapped.
209 * If the fd is to a universal (aka fat) file, the best slice is found and the block is called once with the mapped slice.
210 * If the fd is closed or not mmap()able, the block is never called.
211 *
212 * @param fd
213 * An open file descriptor to a mmap()able file.
214 *
215 * @param callback
216 * A block to call once with the best slice.
217 * Can be NULL. In which case the return value tells you if there was a loadable slice
218 * The slice pointer is only valid during the block invocation.
219 *
220 * @return
221 * Returns zero on success (meaning there is a best slice), otherwise it returns an errno value.
222 * Common returned errors:
223 * EFTYPE - fd content is not a mach-o or fat file
224 * EBADMACHO - fd content is a mach-o file, but it is malformed
225 * EBADARCH - fd content is a mach-o or fat, but none of the slices are loadable
226 */
227extern int macho_best_slice_in_fd(int fd, void (^ _Nullable bestSlice)(const struct mach_header* _Nonnull slice, uint64_t sliceFileOffset, size_t sliceSize)__MACHO_NOESCAPE)
228__API_AVAILABLE(macos(13.0), ios(16.0), tvos(16.0), watchos(8.0));
229
230#endif // __BLOCKS__
231
232
233
234
235
236#if __cplusplus
237}
238#endif
239
240
241#endif // _MACH_O_UTILS_H_
242