1 | #ifndef MACHO_HPP |
---|
2 | #define MACHO_HPP |
---|
3 | |
---|
4 | #include "common.hpp" |
---|
5 | |
---|
6 | #if IS_APPLE |
---|
7 | #include <cstdio> |
---|
8 | #include <cstring> |
---|
9 | #include <type_traits> |
---|
10 | |
---|
11 | #include <mach-o/loader.h> |
---|
12 | #include <mach-o/swap.h> |
---|
13 | #include <mach-o/fat.h> |
---|
14 | |
---|
15 | // Based on https://github.com/AlexDenisov/segment_dumper/blob/master/main.c |
---|
16 | // and https://lowlevelbits.org/parsing-mach-o-files/ |
---|
17 | |
---|
18 | static bool is_magic_64(uint32_t magic) { |
---|
19 | return magic == MH_MAGIC_64 || magic == MH_CIGAM_64; |
---|
20 | } |
---|
21 | |
---|
22 | static bool should_swap_bytes(uint32_t magic) { |
---|
23 | return magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM; |
---|
24 | } |
---|
25 | |
---|
26 | #if defined(__aarch64__) |
---|
27 | #define CURRENT_CPU CPU_TYPE_ARM64 |
---|
28 | #elif defined(__arm__) && defined(__thumb__) |
---|
29 | #define CURRENT_CPU CPU_TYPE_ARM |
---|
30 | #elif defined(__amd64__) |
---|
31 | #define CURRENT_CPU CPU_TYPE_X86_64 |
---|
32 | #elif defined(__i386__) |
---|
33 | #define CURRENT_CPU CPU_TYPE_I386 |
---|
34 | #else |
---|
35 | #error "Unknown CPU architecture" |
---|
36 | #endif |
---|
37 | |
---|
38 | static uintptr_t macho_get_text_vmaddr_from_segments(FILE* obj_file, off_t offset, bool should_swap, uint32_t ncmds) { |
---|
39 | off_t actual_offset = offset; |
---|
40 | for(uint32_t i = 0; i < ncmds; i++) { |
---|
41 | load_command cmd = load_bytes<load_command>(obj_file, actual_offset); |
---|
42 | if(should_swap) { |
---|
43 | swap_load_command(&cmd, NX_UnknownByteOrder); |
---|
44 | } |
---|
45 | if(cmd.cmd == LC_SEGMENT_64) { |
---|
46 | segment_command_64 segment = load_bytes<segment_command_64>(obj_file, actual_offset); |
---|
47 | if(should_swap) { |
---|
48 | swap_segment_command_64(&segment, NX_UnknownByteOrder); |
---|
49 | } |
---|
50 | //printf("segname(64): %s\n", segment.segname); |
---|
51 | //printf(" %d\n", segment.nsects); |
---|
52 | //printf(" %p\n", segment.vmaddr); |
---|
53 | //printf(" %p\n", segment.vmsize); |
---|
54 | if(strcmp(segment.segname, "__TEXT") == 0) { |
---|
55 | return segment.vmaddr; |
---|
56 | } |
---|
57 | } else if(cmd.cmd == LC_SEGMENT) { |
---|
58 | segment_command segment = load_bytes<segment_command>(obj_file, actual_offset); |
---|
59 | if(should_swap) { |
---|
60 | swap_segment_command(&segment, NX_UnknownByteOrder); |
---|
61 | } |
---|
62 | //printf("segname: %s\n", segment.segname); |
---|
63 | if(strcmp(segment.segname, "__TEXT") == 0) { |
---|
64 | return segment.vmaddr; |
---|
65 | } |
---|
66 | } |
---|
67 | actual_offset += cmd.cmdsize; |
---|
68 | } |
---|
69 | // somehow no __TEXT section was found... |
---|
70 | return 0; |
---|
71 | } |
---|
72 | |
---|
73 | static uintptr_t macho_get_text_vmaddr_mach(FILE* obj_file, off_t offset, bool is_64, bool should_swap) { |
---|
74 | uint32_t ncmds; |
---|
75 | off_t load_commands_offset = offset; |
---|
76 | if(is_64) { |
---|
77 | size_t header_size = sizeof(mach_header_64); |
---|
78 | mach_header_64 header = load_bytes<mach_header_64>(obj_file, offset); |
---|
79 | //if(offset != 0) { // if fat the offset will be non-zero, if not fat the offset will be zero |
---|
80 | if(header.cputype != CURRENT_CPU) { |
---|
81 | return 0; |
---|
82 | } |
---|
83 | //} |
---|
84 | if(should_swap) { |
---|
85 | swap_mach_header_64(&header, NX_UnknownByteOrder); |
---|
86 | } |
---|
87 | ncmds = header.ncmds; |
---|
88 | load_commands_offset += header_size; |
---|
89 | } else { |
---|
90 | size_t header_size = sizeof(mach_header); |
---|
91 | mach_header header = load_bytes<mach_header>(obj_file, offset); |
---|
92 | //if(offset != 0) { // if fat the offset will be non-zero, if not fat the offset will be zero |
---|
93 | if(header.cputype != CURRENT_CPU) { |
---|
94 | return 0; |
---|
95 | } |
---|
96 | //} |
---|
97 | if(should_swap) { |
---|
98 | swap_mach_header(&header, NX_UnknownByteOrder); |
---|
99 | } |
---|
100 | ncmds = header.ncmds; |
---|
101 | load_commands_offset += header_size; |
---|
102 | } |
---|
103 | return macho_get_text_vmaddr_from_segments(obj_file, load_commands_offset, should_swap, ncmds); |
---|
104 | } |
---|
105 | |
---|
106 | static uintptr_t macho_get_text_vmaddr_fat(FILE* obj_file, bool should_swap) { |
---|
107 | size_t header_size = sizeof(fat_header); |
---|
108 | size_t arch_size = sizeof(fat_arch); |
---|
109 | fat_header header = load_bytes<fat_header>(obj_file, 0); |
---|
110 | if(should_swap) { |
---|
111 | swap_fat_header(&header, NX_UnknownByteOrder); |
---|
112 | } |
---|
113 | off_t arch_offset = (off_t)header_size; |
---|
114 | uintptr_t text_vmaddr = 0; |
---|
115 | for(uint32_t i = 0; i < header.nfat_arch; i++) { |
---|
116 | fat_arch arch = load_bytes<fat_arch>(obj_file, arch_offset); |
---|
117 | if(should_swap) { |
---|
118 | swap_fat_arch(&arch, 1, NX_UnknownByteOrder); |
---|
119 | } |
---|
120 | off_t mach_header_offset = (off_t)arch.offset; |
---|
121 | arch_offset += arch_size; |
---|
122 | uint32_t magic = load_bytes<uint32_t>(obj_file, mach_header_offset); |
---|
123 | text_vmaddr = macho_get_text_vmaddr_mach( |
---|
124 | obj_file, |
---|
125 | mach_header_offset, |
---|
126 | is_magic_64(magic), |
---|
127 | should_swap_bytes(magic) |
---|
128 | ); |
---|
129 | if(text_vmaddr != 0) { |
---|
130 | return text_vmaddr; |
---|
131 | } |
---|
132 | } |
---|
133 | // If this is reached... something went wrong. The cpu we're on wasn't found. |
---|
134 | return text_vmaddr; |
---|
135 | } |
---|
136 | |
---|
137 | static uintptr_t macho_get_text_vmaddr(const char* path) { |
---|
138 | FILE* obj_file = fopen(path, "rb"); |
---|
139 | if(obj_file == nullptr) { |
---|
140 | throw file_error(); |
---|
141 | } |
---|
142 | uint32_t magic = load_bytes<uint32_t>(obj_file, 0); |
---|
143 | bool is_64 = is_magic_64(magic); |
---|
144 | bool should_swap = should_swap_bytes(magic); |
---|
145 | uintptr_t addr; |
---|
146 | if(magic == FAT_MAGIC || magic == FAT_CIGAM) { |
---|
147 | addr = macho_get_text_vmaddr_fat(obj_file, should_swap); |
---|
148 | } else { |
---|
149 | addr = macho_get_text_vmaddr_mach(obj_file, 0, is_64, should_swap); |
---|
150 | } |
---|
151 | fclose(obj_file); |
---|
152 | return addr; |
---|
153 | } |
---|
154 | |
---|
155 | #endif |
---|
156 | |
---|
157 | #endif |
---|