]> git.saurik.com Git - apple/libc.git/blob - gen/assumes.c
Libc-825.40.1.tar.gz
[apple/libc.git] / gen / assumes.c
1 #include <dispatch/dispatch.h>
2 #include <uuid/uuid.h>
3 #include <sys/types.h>
4 #include <sys/sysctl.h>
5 #include <mach-o/loader.h>
6 #include <mach-o/fat.h>
7 #include <mach-o/arch.h>
8 #include <mach-o/getsect.h>
9 #include <pthread.h>
10 #include <sys/types.h>
11 #include <execinfo.h>
12 #include <stdio.h>
13 #include <dlfcn.h>
14 #include <asl.h>
15 #include <errno.h>
16 #include <pthread.h>
17 #include "assumes.h"
18
19 #define OSX_ASSUME_LOG_REDIRECT_SECT_NAME "__osx_log_func"
20 #define osx_atomic_cmpxchg(p, o, n) __sync_bool_compare_and_swap((p), (o), (n))
21
22 static bool _osx_should_abort_on_assumes = false;
23
24 static const char *
25 _osx_basename(const char *p)
26 {
27 return ((strrchr(p, '/') ? : p - 1) + 1);
28 }
29
30 static void
31 _osx_get_build(char *build, size_t sz)
32 {
33 /* Get the build every time. We used to cache it, but if PID 1 experiences
34 * an assumes() failure before the build has been set, that would mean that
35 * all future failures would get bad build info. So we fetch it every time.
36 * Since assumes() failures are on the slow path anyway, not a huge deal.
37 */
38 int mib[] = { CTL_KERN, KERN_OSVERSION };
39
40 size_t oldsz = sz;
41 int r = sysctl(mib, 2, build, &sz, NULL, 0);
42 if (r == 0 && sz == 1) {
43 (void)strlcpy(build, "99Z999", oldsz);
44 }
45 }
46
47 static void
48 _osx_get_image_uuid(void *hdr, uuid_t uuid)
49 {
50 #if __LP64__
51 struct mach_header_64 *hdr32or64 = (struct mach_header_64 *)hdr;
52 #else
53 struct mach_header *hdr32or64 = (struct mach_header *)hdr;
54 #endif /* __LP64__ */
55
56 size_t i = 0;
57 size_t next = sizeof(*hdr32or64);
58 struct load_command *cur = NULL;
59 for (i = 0; i < hdr32or64->ncmds; i++) {
60 cur = (struct load_command *)((uintptr_t)hdr32or64 + next);
61 if (cur->cmd == LC_UUID) {
62 struct uuid_command *cmd = (struct uuid_command *)cur;
63 uuid_copy(uuid, cmd->uuid);
64 break;
65 }
66
67 next += cur->cmdsize;
68 }
69
70 if (i == hdr32or64->ncmds) {
71 uuid_clear(uuid);
72 }
73 }
74
75 static void
76 _osx_abort_on_assumes_once(void)
77 {
78 /* Embedded boot-args can get pretty long. Let's just hope this is big
79 * enough.
80 */
81 char bootargs[2048];
82 size_t len = sizeof(bootargs) - 1;
83
84 if (sysctlbyname("kern.bootargs", bootargs, &len, NULL, 0) == 0) {
85 if (strnstr(bootargs, "-osx_assumes_fatal", len)) {
86 _osx_should_abort_on_assumes = true;
87 }
88 }
89 }
90
91 static bool
92 _osx_abort_on_assumes(void)
93 {
94 static pthread_once_t once = PTHREAD_ONCE_INIT;
95 bool result = false;
96
97 if (getpid() != 1) {
98 if (getenv("OSX_ASSUMES_FATAL")) {
99 result = true;
100 } else {
101 pthread_once(&once, _osx_abort_on_assumes_once);
102 result = _osx_should_abort_on_assumes;
103 }
104 } else {
105 if (getenv("OSX_ASSUMES_FATAL_PID1")) {
106 result = true;
107 }
108 }
109
110 return result;
111 }
112
113 #if __LP64__
114 static osx_redirect_t
115 _osx_find_log_redirect_func(struct mach_header_64 *hdr)
116 {
117 osx_redirect_t result = NULL;
118
119 char name[128];
120 unsigned long size = 0;
121 uint8_t *data = getsectiondata(hdr, "__TEXT", OSX_ASSUME_LOG_REDIRECT_SECT_NAME, &size);
122 if (data && size < sizeof(name) - 2) {
123 /* Ensure NUL termination. */
124 (void)strlcpy(name, (const char *)data, size + 1);
125 result = dlsym(RTLD_DEFAULT, name);
126 }
127
128 return result;
129 }
130 #else
131 static osx_redirect_t
132 _osx_find_log_redirect_func(struct mach_header *hdr)
133 {
134 osx_redirect_t result = NULL;
135
136 char name[128];
137 unsigned long size = 0;
138 uint8_t *data = getsectiondata(hdr, "__TEXT", OSX_ASSUME_LOG_REDIRECT_SECT_NAME, &size);
139 if (data && size < sizeof(name) - 2) {
140 (void)strlcpy(name, (const char *)data, size + 1);
141 result = dlsym(RTLD_DEFAULT, name);
142 }
143
144 return result;
145 }
146 #endif
147
148 static bool
149 _osx_log_redirect(void *hdr, const char *msg)
150 {
151 bool result = false;
152
153 osx_redirect_t redirect_func = _osx_find_log_redirect_func(hdr);
154 if (redirect_func) {
155 result = redirect_func(msg);
156 }
157
158 return result;
159 }
160
161 __attribute__((always_inline))
162 static void
163 _osx_construct_message(const char *prefix, uint64_t code, aslmsg asl_message, Dl_info *info, char *buff, size_t sz)
164 {
165 const char *image_name = NULL;
166 uintptr_t offset = 0;
167 uuid_string_t uuid_str;
168
169 void *ret = __builtin_return_address(0);
170 if (dladdr(ret, info)) {
171 uuid_t uuid;
172 _osx_get_image_uuid(info->dli_fbase, uuid);
173
174 uuid_unparse(uuid, uuid_str);
175 image_name = _osx_basename(info->dli_fname);
176
177 offset = ret - info->dli_fbase;
178 }
179
180 char name[256];
181 (void)snprintf(name, sizeof(name), "com.apple.assumes.%s", image_name);
182
183 char sig[64];
184 (void)snprintf(sig, sizeof(sig), "%s:%lu", uuid_str, offset);
185
186 char result[24];
187 (void)snprintf(result, sizeof(result), "0x%llx", code);
188
189 char build[16];
190 size_t bsz = sizeof(build);
191 _osx_get_build(build, bsz);
192
193 (void)snprintf(buff, sz, "%s: %s: %s + %lu [%s]: %s", prefix, build, image_name, offset, uuid_str, result);
194
195 (void)asl_set(asl_message, "com.apple.message.domain", name);
196 (void)asl_set(asl_message, "com.apple.message.signature", sig);
197 (void)asl_set(asl_message, "com.apple.message.value", result);
198 }
199
200 void
201 _osx_assumes_log(uint64_t code)
202 {
203 aslmsg asl_message = asl_new(ASL_TYPE_MSG);
204 if (asl_message) {
205 Dl_info info;
206 char message[256];
207 _osx_construct_message("Bug", code, asl_message, &info, message, sizeof(message));
208 if (!_osx_log_redirect(info.dli_fbase, message)) {
209 /* MessageTracer messages aren't logged to the regular syslog store,
210 * so we log the message without any MessageTracer attributes so
211 * that we can see it in our regular syslog.
212 */
213 (void)asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s", message);
214 (void)asl_log(NULL, asl_message, ASL_LEVEL_ERR, "%s", message);
215 }
216
217 asl_free(asl_message);
218 }
219
220 if (_osx_abort_on_assumes()) {
221 __builtin_trap();
222 }
223 }
224
225 char *
226 _osx_assert_log(uint64_t code)
227 {
228 char *result = NULL;
229
230 aslmsg asl_message = asl_new(ASL_TYPE_MSG);
231 if (asl_message) {
232 Dl_info info;
233 char message[256];
234 _osx_construct_message("Fatal bug", code, asl_message, &info, message, sizeof(message));
235 if (!_osx_log_redirect(info.dli_fbase, message)) {
236 (void)asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s", message);
237 (void)asl_log(NULL, asl_message, ASL_LEVEL_ERR, "%s", message);
238 }
239
240 asl_free(asl_message);
241 result = strdup(message);
242 }
243
244 #if LIBC_NO_LIBCRASHREPORTERCLIENT
245 /* There is no crash report information facility on embedded, which is
246 * really regrettable. Fortunately, all we need to capture is the value
247 * which tripped up the assertion. We can just stuff that into the thread's
248 * name.
249 */
250 char name[64];
251 (void)pthread_getname_np(pthread_self(), name, sizeof(name));
252
253 char newname[64];
254 if (strlen(name) == 0) {
255 (void)snprintf(newname, sizeof(newname), "[Fatal bug: 0x%llx]", code);
256 } else {
257 (void)snprintf(newname, sizeof(newname), "%s [Fatal bug: 0x%llx]", name, code);
258 }
259
260 (void)pthread_setname_np(newname);
261 #endif
262
263 return result;
264 }
265
266 void
267 _osx_assumes_log_ctx(osx_log_callout_t callout, void *ctx, uint64_t code)
268 {
269 aslmsg asl_message = asl_new(ASL_TYPE_MSG);
270 if (asl_message) {
271 Dl_info info;
272 char message[256];
273 _osx_construct_message("Bug", code, asl_message, &info, message, sizeof(message));
274
275 (void)callout(asl_message, ctx, message);
276 asl_free(asl_message);
277 }
278
279 if (_osx_abort_on_assumes()) {
280 __builtin_trap();
281 }
282 }
283
284 char *
285 _osx_assert_log_ctx(osx_log_callout_t callout, void *ctx, uint64_t code)
286 {
287 char *result = NULL;
288
289 aslmsg asl_message = asl_new(ASL_TYPE_MSG);
290 if (asl_message) {
291 Dl_info info;
292 char message[256];
293 _osx_construct_message("Fatal bug", code, asl_message, &info, message, sizeof(message));
294
295 (void)callout(asl_message, ctx, message);
296 asl_free(asl_message);
297 result = strdup(message);
298 }
299 }
300
301 extern void
302 _osx_avoid_tail_call(void)
303 {
304 // no-op
305 }