v / vlib / v2 / gen / x64 / macho_tiny.v
709 lines · 667 sloc · 17.73 KB · e7738c112c787d477501fa4a87edd0e1d72159bd
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module x64
6
7import encoding.binary
8
9pub const macos_tiny_not_eligible_prefix = 'macOS tiny object is not eligible: '
10
11const macos_tiny_builtin_string_plus_symbol = '_builtin__string__+'
12const macos_tiny_builtin_i64_str_symbol = '_builtin__i64__str'
13const macos_tiny_malloc_symbol = '_malloc'
14const macos_tiny_exit_symbol = '_exit'
15
16struct MachOTextRange {
17 name string
18 start u64
19 end u64
20}
21
22struct MachODataRange {
23 section ObjectSection
24 start u64
25 end u64
26}
27
28struct MachOTinyReachable {
29mut:
30 names []string
31 data []MachODataRange
32 undefined map[string]bool
33}
34
35struct MachOTinyObjectWriter {
36 macho &MachOObject
37}
38
39struct MachOTinyRuntimeReloc {
40 addr int
41 symbol string
42}
43
44pub fn (mut g Gen) write_macos_tiny_object(path string) ! {
45 if g.obj_format != .macho {
46 return error('macOS tiny object writing requires Mach-O object output')
47 }
48 if g.abi != .sysv {
49 return error('macOS tiny object writing requires SysV x64 code generation')
50 }
51 mut writer := MachOTinyObjectWriter{
52 macho: g.macho
53 }
54 writer.write(path)!
55}
56
57fn (mut w MachOTinyObjectWriter) write(path string) ! {
58 mut reachable := w.collect_reachable()!
59 mut out := MachOObject.new()
60 mut symbol_indices := map[string]int{}
61 mut func_offsets := map[string]u64{}
62 mut runtime_symbols := []string{}
63 mut runtime_relocs := []MachOTinyRuntimeReloc{}
64
65 for name in reachable.names {
66 range := w.text_range(name)!
67 func_offsets[name] = u64(out.text_data.len)
68 out.text_data << w.macho.text_data[int(range.start)..int(range.end)]
69 }
70 macho_tiny_add_runtime_helpers(mut out, mut reachable, mut func_offsets, mut runtime_symbols, mut
71 runtime_relocs)
72
73 mut data_offsets := map[string]u64{}
74 w.copy_data_ranges(reachable.data, .rodata, mut out.rodata, mut data_offsets)!
75 w.copy_data_ranges(reachable.data, .data, mut out.data_data, mut data_offsets)!
76
77 for name in reachable.names {
78 symbol_indices[name] = out.add_symbol(name, func_offsets[name],
79 macho_tiny_defined_symbol_is_external(name), ObjectFormat.macho.section_index(.text))
80 }
81 for name in runtime_symbols {
82 symbol_indices[name] = out.add_symbol(name, func_offsets[name],
83 macho_tiny_defined_symbol_is_external(name), ObjectFormat.macho.section_index(.text))
84 }
85 mut data_names := data_offsets.keys()
86 data_names.sort()
87 for name in data_names {
88 orig := w.symbol_by_name(name)!
89 section := w.symbol_object_section(orig)!
90 symbol_indices[name] = out.add_symbol(name, data_offsets[name],
91 macho_tiny_defined_symbol_is_external(name), ObjectFormat.macho.section_index(section))
92 }
93 mut undefined_names := reachable.undefined.keys()
94 undefined_names.sort()
95 for name in undefined_names {
96 symbol_indices[name] = out.add_undefined(name)
97 }
98 for name in reachable.names {
99 range := w.text_range(name)!
100 new_base := func_offsets[name]
101 for reloc in w.macho.relocs {
102 if reloc.addr < int(range.start) || reloc.addr >= int(range.end) {
103 continue
104 }
105 sym := w.reloc_symbol(reloc)!
106 sym_idx := symbol_indices[sym.name] or {
107 return macos_tiny_not_eligible('symbol `${sym.name}` is not resolved by the tiny object')
108 }
109 out_reloc_addr := int(new_base + u64(reloc.addr - int(range.start)))
110 if macho_tiny_resolve_relocation_locally(sym.name, reloc) {
111 macho_tiny_patch_rel32_local(mut out.text_data, out_reloc_addr,
112 int(func_offsets[sym.name]))
113 continue
114 }
115 out.add_reloc(out_reloc_addr, sym_idx, reloc.type_, reloc.pcrel, reloc.length)
116 }
117 }
118 for reloc in runtime_relocs {
119 sym_idx := symbol_indices[reloc.symbol] or {
120 return macos_tiny_not_eligible('runtime helper symbol `${reloc.symbol}` is not resolved by the tiny object')
121 }
122 out.add_reloc(reloc.addr, sym_idx, x86_64_reloc_branch, true, 2)
123 }
124
125 out.write(path)
126}
127
128fn (mut w MachOTinyObjectWriter) collect_reachable() !MachOTinyReachable {
129 _ = w.text_range('_main') or { return macos_tiny_not_eligible('missing _main symbol') }
130 mut selected := []string{}
131 mut seen := map[string]bool{}
132 mut queue := ['_main']
133 mut selected_data := []MachODataRange{}
134 mut undefined := map[string]bool{}
135 for queue.len > 0 {
136 name := queue[0]
137 queue.delete(0)
138 if seen[name] {
139 continue
140 }
141 if macho_tiny_is_module_init_symbol(name) {
142 return macos_tiny_not_eligible('module init symbol `${name}` is not supported')
143 }
144 range := w.text_range(name)!
145 seen[name] = true
146 selected << name
147 for reloc in w.macho.relocs {
148 if reloc.addr < int(range.start) || reloc.addr >= int(range.end) {
149 continue
150 }
151 validate_macho_tiny_relocation(reloc)!
152 sym := w.reloc_symbol(reloc)!
153 if sym.sect == ObjectFormat.macho.section_index(.text) {
154 if !seen[sym.name] {
155 queue << sym.name
156 }
157 } else if sym.sect == ObjectFormat.macho.section_index(.rodata) {
158 w.add_data_range(mut selected_data, .rodata, sym.value)!
159 } else if sym.sect == ObjectFormat.macho.section_index(.data) {
160 if x64_main_argc_global_name(sym.name) || x64_main_argv_global_name(sym.name) {
161 return macos_tiny_not_eligible('arguments() requires hosted argc/argv; macOS tiny _main argv is not implemented yet')
162 }
163 w.add_data_range(mut selected_data, .data, sym.value)!
164 } else if sym.sect == 0 && sym.type_ == 0x01 {
165 undefined[sym.name] = true
166 } else {
167 return macos_tiny_not_eligible('relocation to symbol `${sym.name}` in Mach-O section ${sym.sect} is not supported')
168 }
169 }
170 }
171 return MachOTinyReachable{
172 names: selected
173 data: selected_data
174 undefined: undefined
175 }
176}
177
178fn validate_macho_tiny_relocation(reloc MachORelocationInfo) ! {
179 if !reloc.pcrel || reloc.length != 2 {
180 return macos_tiny_not_eligible('non-pcrel or non-32-bit relocation at __text+${reloc.addr} is not supported')
181 }
182 if reloc.type_ !in [x86_64_reloc_signed, x86_64_reloc_branch, x86_64_reloc_got_load,
183 x86_64_reloc_got] {
184 return macos_tiny_not_eligible('Mach-O relocation type ${reloc.type_} is not supported')
185 }
186}
187
188fn (w MachOTinyObjectWriter) text_range(name string) !MachOTextRange {
189 ranges := w.text_symbol_ranges()
190 for range in ranges {
191 if range.name == name {
192 return range
193 }
194 }
195 return error('missing text symbol `${name}`')
196}
197
198fn (w MachOTinyObjectWriter) text_symbol_ranges() []MachOTextRange {
199 mut syms := []MachOSymbol{}
200 for sym in w.macho.symbols {
201 if sym.name != '' && sym.sect == ObjectFormat.macho.section_index(.text) {
202 syms << sym
203 }
204 }
205 for i := 1; i < syms.len; i++ {
206 mut j := i
207 for j > 0 && syms[j - 1].value > syms[j].value {
208 syms[j - 1], syms[j] = syms[j], syms[j - 1]
209 j--
210 }
211 }
212 mut ranges := []MachOTextRange{}
213 for i, sym in syms {
214 mut end := u64(w.macho.text_data.len)
215 for j := i + 1; j < syms.len; j++ {
216 if syms[j].value > sym.value {
217 end = syms[j].value
218 break
219 }
220 }
221 if end > sym.value {
222 ranges << MachOTextRange{
223 name: sym.name
224 start: sym.value
225 end: end
226 }
227 }
228 }
229 return ranges
230}
231
232fn (w MachOTinyObjectWriter) reloc_symbol(reloc MachORelocationInfo) !MachOSymbol {
233 if reloc.sym_idx < 0 || reloc.sym_idx >= w.macho.symbols.len {
234 return error('macOS tiny object relocation references invalid symbol index ${reloc.sym_idx}')
235 }
236 return w.macho.symbols[reloc.sym_idx]
237}
238
239fn (w MachOTinyObjectWriter) symbol_by_name(name string) !MachOSymbol {
240 idx := w.macho.sym_by_name[name] or { return error('missing symbol `${name}`') }
241 if idx < 0 || idx >= w.macho.symbols.len {
242 return error('invalid symbol index ${idx} for `${name}`')
243 }
244 return w.macho.symbols[idx]
245}
246
247fn (w MachOTinyObjectWriter) symbol_object_section(sym MachOSymbol) !ObjectSection {
248 if sym.sect == ObjectFormat.macho.section_index(.rodata) {
249 return .rodata
250 }
251 if sym.sect == ObjectFormat.macho.section_index(.data) {
252 return .data
253 }
254 if sym.sect == ObjectFormat.macho.section_index(.text) {
255 return .text
256 }
257 return error('symbol `${sym.name}` is not in a known Mach-O section')
258}
259
260fn (mut w MachOTinyObjectWriter) add_data_range(mut ranges []MachODataRange, section ObjectSection, start u64) ! {
261 end := w.data_symbol_range_end(section, start)!
262 for range in ranges {
263 if range.section == section && range.start == start && range.end == end {
264 return
265 }
266 }
267 ranges << MachODataRange{
268 section: section
269 start: start
270 end: end
271 }
272}
273
274fn (w MachOTinyObjectWriter) data_symbol_range_end(section ObjectSection, start u64) !u64 {
275 section_idx := ObjectFormat.macho.section_index(section)
276 section_len := match section {
277 .rodata { u64(w.macho.rodata.len) }
278 .data { u64(w.macho.data_data.len) }
279 else { u64(0) }
280 }
281
282 mut end := section_len
283 for sym in w.macho.symbols {
284 if sym.sect == section_idx && sym.value > start && sym.value < end {
285 end = sym.value
286 }
287 }
288 if end < start {
289 return error('invalid Mach-O data symbol range')
290 }
291 return end
292}
293
294fn (w MachOTinyObjectWriter) copy_data_ranges(ranges []MachODataRange, section ObjectSection, mut out []u8, mut offsets map[string]u64) ! {
295 for dr in ranges {
296 if dr.section != section {
297 continue
298 }
299 align := int(w.data_section_alignment(section))
300 for align > 1 && out.len % align != int(dr.start % u64(align)) {
301 out << u8(0)
302 }
303 off := u64(out.len)
304 section_idx := ObjectFormat.macho.section_index(section)
305 for sym in w.macho.symbols {
306 if sym.sect == section_idx && sym.value == dr.start {
307 offsets[sym.name] = off
308 }
309 }
310 match section {
311 .rodata {
312 out << w.macho.rodata[int(dr.start)..int(dr.end)]
313 }
314 .data {
315 out << w.macho.data_data[int(dr.start)..int(dr.end)]
316 }
317 else {
318 return error('macOS tiny object cannot copy ${section} as data')
319 }
320 }
321 }
322}
323
324fn (w MachOTinyObjectWriter) data_section_alignment(section ObjectSection) u64 {
325 return match section {
326 .data { u64(8) }
327 .rodata { u64(4) }
328 else { u64(1) }
329 }
330}
331
332fn macho_tiny_defined_symbol_is_external(name string) bool {
333 return name == '_main'
334}
335
336fn macho_tiny_resolve_relocation_locally(name string, reloc MachORelocationInfo) bool {
337 return name in [macos_tiny_builtin_string_plus_symbol, macos_tiny_builtin_i64_str_symbol]
338 && reloc.type_ == x86_64_reloc_branch && reloc.pcrel && reloc.length == 2
339}
340
341fn macho_tiny_add_runtime_helpers(mut out MachOObject, mut reachable MachOTinyReachable, mut func_offsets map[string]u64, mut runtime_symbols []string, mut runtime_relocs []MachOTinyRuntimeReloc) {
342 if reachable.undefined[macos_tiny_builtin_i64_str_symbol] {
343 reachable.undefined.delete(macos_tiny_builtin_i64_str_symbol)
344 reachable.undefined[macos_tiny_malloc_symbol] = true
345 reachable.undefined[macos_tiny_exit_symbol] = true
346 func_offsets[macos_tiny_builtin_i64_str_symbol] = u64(out.text_data.len)
347 runtime_symbols << macos_tiny_builtin_i64_str_symbol
348 macho_tiny_emit_i64_str(mut out.text_data, mut runtime_relocs)
349 }
350 if reachable.undefined[macos_tiny_builtin_string_plus_symbol] {
351 reachable.undefined.delete(macos_tiny_builtin_string_plus_symbol)
352 reachable.undefined[macos_tiny_malloc_symbol] = true
353 reachable.undefined[macos_tiny_exit_symbol] = true
354 func_offsets[macos_tiny_builtin_string_plus_symbol] = u64(out.text_data.len)
355 runtime_symbols << macos_tiny_builtin_string_plus_symbol
356 macho_tiny_emit_string_plus(mut out.text_data, mut runtime_relocs)
357 }
358}
359
360fn macho_tiny_emit_i64_str(mut text []u8, mut runtime_relocs []MachOTinyRuntimeReloc) {
361 text << [
362 u8(0x57), // push rdi, input value
363 0xbf,
364 0x20,
365 0x00,
366 0x00,
367 0x00, // mov edi, 32
368 ]
369 macho_tiny_emit_call_reloc(mut text, mut runtime_relocs, macos_tiny_malloc_symbol)
370 text << [
371 u8(0x48),
372 0x85,
373 0xc0, // test rax, rax
374 ]
375 malloc_ok := macho_tiny_emit_jcc32(mut text, 0x85) // jne ok
376 text << [
377 u8(0xbf),
378 0x01,
379 0x00,
380 0x00,
381 0x00, // mov edi, 1
382 ]
383 macho_tiny_emit_call_reloc(mut text, mut runtime_relocs, macos_tiny_exit_symbol)
384 text << [u8(0x0f), 0x0b] // ud2
385 ok_start := text.len
386 macho_tiny_patch_rel32_local(mut text, malloc_ok, ok_start)
387
388 text << [
389 u8(0x5f), // pop rdi
390 0x4c,
391 0x8d,
392 0x40,
393 0x1f, // lea r8, [rax+31]
394 0x41,
395 0xc6,
396 0x00,
397 0x00, // mov byte ptr [r8], 0
398 0x48,
399 0x89,
400 0xf8, // mov rax, rdi
401 0x45,
402 0x31,
403 0xc9, // xor r9d, r9d
404 0x45,
405 0x31,
406 0xd2, // xor r10d, r10d
407 0x48,
408 0x85,
409 0xc0, // test rax, rax
410 ]
411 non_negative := macho_tiny_emit_jcc32(mut text, 0x89) // jns non_negative
412 text << [
413 u8(0x41),
414 0xb2,
415 0x01, // mov r10b, 1
416 0x48,
417 0xf7,
418 0xd8, // neg rax
419 ]
420 non_negative_target := text.len
421 text << [
422 u8(0x48),
423 0x85,
424 0xc0, // test rax, rax
425 ]
426 non_zero := macho_tiny_emit_jcc32(mut text, 0x85) // jne loop
427 text << [
428 u8(0x49),
429 0xff,
430 0xc8, // dec r8
431 0x41,
432 0xc6,
433 0x00,
434 0x30, // mov byte ptr [r8], '0'
435 0x41,
436 0xb9,
437 0x01,
438 0x00,
439 0x00,
440 0x00, // mov r9d, 1
441 ]
442 maybe_sign_jump := macho_tiny_emit_jmp32(mut text)
443 loop_start := text.len
444 text << [
445 u8(0x31),
446 0xd2, // xor edx, edx
447 0xb9,
448 0x0a,
449 0x00,
450 0x00,
451 0x00, // mov ecx, 10
452 0x48,
453 0xf7,
454 0xf1, // div rcx
455 0x80,
456 0xc2,
457 0x30, // add dl, '0'
458 0x49,
459 0xff,
460 0xc8, // dec r8
461 0x41,
462 0x88,
463 0x10, // mov [r8], dl
464 0x49,
465 0xff,
466 0xc1, // inc r9
467 0x48,
468 0x85,
469 0xc0, // test rax, rax
470 ]
471 loop_more := macho_tiny_emit_jcc32(mut text, 0x85) // jne loop
472 maybe_sign := text.len
473 text << [
474 u8(0x45),
475 0x84,
476 0xd2, // test r10b, r10b
477 ]
478 done_digits := macho_tiny_emit_jcc32(mut text, 0x84) // je done
479 text << [
480 u8(0x49),
481 0xff,
482 0xc8, // dec r8
483 0x41,
484 0xc6,
485 0x00,
486 0x2d, // mov byte ptr [r8], '-'
487 0x49,
488 0xff,
489 0xc1, // inc r9
490 ]
491 done_digits_target := text.len
492 text << [
493 u8(0x4c),
494 0x89,
495 0xc0, // mov rax, r8
496 0x4c,
497 0x89,
498 0xca, // mov rdx, r9
499 0xc3, // ret
500 ]
501 macho_tiny_patch_rel32_local(mut text, non_negative, non_negative_target)
502 macho_tiny_patch_rel32_local(mut text, non_zero, loop_start)
503 macho_tiny_patch_rel32_local(mut text, maybe_sign_jump, maybe_sign)
504 macho_tiny_patch_rel32_local(mut text, loop_more, loop_start)
505 macho_tiny_patch_rel32_local(mut text, done_digits, done_digits_target)
506}
507
508fn macho_tiny_emit_string_plus(mut text []u8, mut runtime_relocs []MachOTinyRuntimeReloc) {
509 text << [
510 u8(0x57), // push rdi, first string ptr
511 0x56, // push rsi, first string len
512 0x52, // push rdx, second string ptr
513 0x51, // push rcx, second string len
514 0x48,
515 0x83,
516 0xec,
517 0x08, // sub rsp, 8, align before libSystem calls
518 0x44,
519 0x8b,
520 0x44,
521 0x24,
522 0x18, // mov r8d, [rsp+24]
523 0x44,
524 0x03,
525 0x44,
526 0x24,
527 0x08, // add r8d, [rsp+8]
528 ]
529 len_overflow := macho_tiny_emit_jcc32(mut text, 0x82) // jc fail
530 text << [
531 u8(0x4d),
532 0x89,
533 0xc1, // mov r9, r8
534 0x49,
535 0x83,
536 0xc1,
537 0x01, // add r9, 1
538 ]
539 alloc_overflow := macho_tiny_emit_jcc32(mut text, 0x82) // jc fail
540 text << [
541 u8(0x4c),
542 0x89,
543 0xcf, // mov rdi, r9
544 ]
545 macho_tiny_emit_call_reloc(mut text, mut runtime_relocs, macos_tiny_malloc_symbol)
546 text << [
547 u8(0x48),
548 0x85,
549 0xc0, // test rax, rax
550 ]
551 malloc_ok := macho_tiny_emit_jcc32(mut text, 0x85) // jne ok
552 fail_start := text.len
553 text << [
554 u8(0xbf),
555 0x01,
556 0x00,
557 0x00,
558 0x00, // mov edi, 1
559 ]
560 macho_tiny_emit_call_reloc(mut text, mut runtime_relocs, macos_tiny_exit_symbol)
561 text << [u8(0x0f), 0x0b] // ud2
562 ok_start := text.len
563 macho_tiny_patch_rel32_local(mut text, len_overflow, fail_start)
564 macho_tiny_patch_rel32_local(mut text, alloc_overflow, fail_start)
565 macho_tiny_patch_rel32_local(mut text, malloc_ok, ok_start)
566
567 text << [
568 u8(0x49),
569 0x89,
570 0xc2, // mov r10, rax
571 0x49,
572 0x89,
573 0xc0, // mov r8, rax
574 0x48,
575 0x8b,
576 0x74,
577 0x24,
578 0x20, // mov rsi, [rsp+32]
579 0x8b,
580 0x4c,
581 0x24,
582 0x18, // mov ecx, [rsp+24]
583 0x48,
584 0x85,
585 0xc9, // test rcx, rcx
586 ]
587 copy_a_done := macho_tiny_emit_jcc32(mut text, 0x84) // jz done
588 copy_a_loop := text.len
589 text << [
590 u8(0x8a),
591 0x16, // mov dl, [rsi]
592 0x41,
593 0x88,
594 0x10, // mov [r8], dl
595 0x48,
596 0xff,
597 0xc6, // inc rsi
598 0x49,
599 0xff,
600 0xc0, // inc r8
601 0x48,
602 0xff,
603 0xc9, // dec rcx
604 ]
605 copy_a_continue := macho_tiny_emit_jcc32(mut text, 0x85) // jne loop
606 copy_a_done_target := text.len
607 macho_tiny_patch_rel32_local(mut text, copy_a_done, copy_a_done_target)
608 macho_tiny_patch_rel32_local(mut text, copy_a_continue, copy_a_loop)
609
610 text << [
611 u8(0x48),
612 0x8b,
613 0x74,
614 0x24,
615 0x10, // mov rsi, [rsp+16]
616 0x8b,
617 0x4c,
618 0x24,
619 0x08, // mov ecx, [rsp+8]
620 0x48,
621 0x85,
622 0xc9, // test rcx, rcx
623 ]
624 copy_b_done := macho_tiny_emit_jcc32(mut text, 0x84) // jz done
625 copy_b_loop := text.len
626 text << [
627 u8(0x8a),
628 0x16, // mov dl, [rsi]
629 0x41,
630 0x88,
631 0x10, // mov [r8], dl
632 0x48,
633 0xff,
634 0xc6, // inc rsi
635 0x49,
636 0xff,
637 0xc0, // inc r8
638 0x48,
639 0xff,
640 0xc9, // dec rcx
641 ]
642 copy_b_continue := macho_tiny_emit_jcc32(mut text, 0x85) // jne loop
643 copy_b_done_target := text.len
644 macho_tiny_patch_rel32_local(mut text, copy_b_done, copy_b_done_target)
645 macho_tiny_patch_rel32_local(mut text, copy_b_continue, copy_b_loop)
646
647 text << [
648 u8(0x41),
649 0xc6,
650 0x00,
651 0x00, // mov byte ptr [r8], 0
652 0x8b,
653 0x44,
654 0x24,
655 0x18, // mov eax, [rsp+24]
656 0x03,
657 0x44,
658 0x24,
659 0x08, // add eax, [rsp+8]
660 0x48,
661 0x89,
662 0xc2, // mov rdx, rax
663 0x4c,
664 0x89,
665 0xd0, // mov rax, r10
666 0x48,
667 0x83,
668 0xc4,
669 0x28, // add rsp, 40
670 0xc3, // ret
671 ]
672}
673
674fn macho_tiny_emit_call_reloc(mut text []u8, mut runtime_relocs []MachOTinyRuntimeReloc, symbol string) {
675 text << u8(0xe8)
676 field_off := text.len
677 text << [u8(0), 0, 0, 0]
678 runtime_relocs << MachOTinyRuntimeReloc{
679 addr: field_off
680 symbol: symbol
681 }
682}
683
684fn macho_tiny_emit_jcc32(mut text []u8, opcode u8) int {
685 text << [u8(0x0f), opcode]
686 field_off := text.len
687 text << [u8(0), 0, 0, 0]
688 return field_off
689}
690
691fn macho_tiny_emit_jmp32(mut text []u8) int {
692 text << u8(0xe9)
693 field_off := text.len
694 text << [u8(0), 0, 0, 0]
695 return field_off
696}
697
698fn macho_tiny_patch_rel32_local(mut text []u8, field_off int, target_off int) {
699 disp := i64(target_off) - i64(field_off + 4)
700 binary.little_endian_put_u32(mut text[field_off..field_off + 4], u32(i32(disp)))
701}
702
703fn macho_tiny_is_module_init_symbol(name string) bool {
704 return name.ends_with('__init') && name != '_main'
705}
706
707fn macos_tiny_not_eligible(message string) IError {
708 return error(macos_tiny_not_eligible_prefix + message)
709}
710