1 #ifndef POSTJECT_API_H_
2 #define POSTJECT_API_H_
3
4 #include <stdbool.h>
5 #include <stddef.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #if defined(__APPLE__) && defined(__MACH__)
10 #include <mach-o/dyld.h>
11 #include <mach-o/getsect.h>
12 #elif defined(__linux__)
13 #include <elf.h>
14 #include <link.h>
15 #include <sys/param.h>
16 #elif defined(_WIN32)
17 #include <windows.h>
18 #endif
19
20 #ifndef POSTJECT_SENTINEL_FUSE
21 #define POSTJECT_SENTINEL_FUSE \
22 "POSTJECT_SENTINEL_fce680ab2cc467b6e072b8b5df1996b2"
23 #endif
24
25 struct postject_options {
26 const char* elf_section_name;
27 const char* macho_framework_name;
28 const char* macho_section_name;
29 const char* macho_segment_name;
30 const char* pe_resource_name;
31 };
32
postject_options_init(struct postject_options * options)33 inline void postject_options_init(struct postject_options* options) {
34 options->elf_section_name = NULL;
35 options->macho_framework_name = NULL;
36 options->macho_section_name = NULL;
37 options->macho_segment_name = NULL;
38 options->pe_resource_name = NULL;
39 }
40
postject_has_resource()41 static inline bool postject_has_resource() {
42 static const volatile char* sentinel = POSTJECT_SENTINEL_FUSE ":0";
43 return sentinel[sizeof(POSTJECT_SENTINEL_FUSE)] == '1';
44 }
45
46 #if defined(__linux__)
postject__dl_iterate_phdr_callback(struct dl_phdr_info * info,size_t size,void * data)47 static int postject__dl_iterate_phdr_callback(struct dl_phdr_info* info,
48 size_t size,
49 void* data) {
50 // Snag the dl_phdr_info struct for the main program, then stop iterating
51 *((struct dl_phdr_info*)data) = *info;
52 return 1;
53 }
54 #endif
55
postject_find_resource(const char * name,size_t * size,const struct postject_options * options)56 static const void* postject_find_resource(
57 const char* name,
58 size_t* size,
59 const struct postject_options* options) {
60 // Always zero out the size pointer to start
61 if (size != NULL) {
62 *size = 0;
63 }
64
65 #if defined(__APPLE__) && defined(__MACH__)
66 char* section_name = NULL;
67 const char* segment_name = "__POSTJECT";
68
69 if (options != NULL && options->macho_segment_name != NULL) {
70 segment_name = options->macho_segment_name;
71 }
72
73 if (options != NULL && options->macho_section_name != NULL) {
74 name = options->macho_section_name;
75 } else if (strncmp(name, "__", 2) != 0) {
76 // Automatically prepend __ to match naming convention
77 section_name = (char*)malloc(strlen(name) + 3);
78 if (section_name == NULL) {
79 return NULL;
80 }
81 strcpy(section_name, "__");
82 strcat(section_name, name);
83 }
84
85 unsigned long section_size;
86 char* ptr = NULL;
87 if (options != NULL && options->macho_framework_name != NULL) {
88 #ifdef __clang__
89 #pragma clang diagnostic push
90 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
91 #endif
92 ptr = getsectdatafromFramework(options->macho_framework_name, segment_name,
93 section_name != NULL ? section_name : name,
94 §ion_size);
95 } else {
96 ptr = getsectdata(segment_name, section_name != NULL ? section_name : name,
97 §ion_size);
98 #ifdef __clang__
99 #pragma clang diagnostic pop
100 #endif
101
102 if (ptr != NULL) {
103 // Add the "virtual memory address slide" amount to ensure a valid pointer
104 // in cases where the virtual memory address have been adjusted by the OS.
105 //
106 // NOTE - `getsectdataFromFramework` already handles this adjustment for
107 // us, which is why we only do it for `getsectdata`, see:
108 // https://web.archive.org/web/20220613234007/https://opensource.apple.com/source/cctools/cctools-590/libmacho/getsecbyname.c.auto.html
109 ptr += _dyld_get_image_vmaddr_slide(0);
110 }
111 }
112
113 free(section_name);
114
115 if (size != NULL) {
116 *size = (size_t)section_size;
117 }
118
119 return ptr;
120 #elif defined(__linux__)
121
122 if (options != NULL && options->elf_section_name != NULL) {
123 name = options->elf_section_name;
124 }
125
126 struct dl_phdr_info main_program_info;
127 dl_iterate_phdr(postject__dl_iterate_phdr_callback, &main_program_info);
128
129 uintptr_t p = (uintptr_t)main_program_info.dlpi_phdr;
130 size_t n = main_program_info.dlpi_phnum;
131 uintptr_t base_addr = main_program_info.dlpi_addr;
132
133 // iterate program header
134 for (; n > 0; n--, p += sizeof(ElfW(Phdr))) {
135 ElfW(Phdr)* phdr = (ElfW(Phdr)*)p;
136
137 // skip everything but notes
138 if (phdr->p_type != PT_NOTE) {
139 continue;
140 }
141
142 // note segment starts at base address + segment virtual address
143 uintptr_t pos = (base_addr + phdr->p_vaddr);
144 uintptr_t end = (pos + phdr->p_memsz);
145
146 // iterate through segment until we reach the end
147 while (pos < end) {
148 if (pos + sizeof(ElfW(Nhdr)) > end) {
149 break; // invalid
150 }
151
152 ElfW(Nhdr)* note = (ElfW(Nhdr)*)(uintptr_t)pos;
153 if (note->n_namesz != 0 && note->n_descsz != 0 &&
154 strncmp((char*)(pos + sizeof(ElfW(Nhdr))), (char*)name,
155 sizeof(name)) == 0) {
156 *size = note->n_descsz;
157 // advance past note header and aligned name
158 // to get to description data
159 return (void*)((uintptr_t)note + sizeof(ElfW(Nhdr)) +
160 roundup(note->n_namesz, 4));
161 }
162
163 pos += (sizeof(ElfW(Nhdr)) + roundup(note->n_namesz, 4) +
164 roundup(note->n_descsz, 4));
165 }
166 }
167 return NULL;
168
169 #elif defined(_WIN32)
170 void* ptr = NULL;
171 char* resource_name = NULL;
172
173 if (options != NULL && options->pe_resource_name != NULL) {
174 name = options->pe_resource_name;
175 } else {
176 // Automatically uppercase the resource name or it won't be found
177 resource_name = (char*)malloc(strlen(name) + 1);
178 if (resource_name == NULL) {
179 return NULL;
180 }
181 strcpy_s(resource_name, strlen(name) + 1, name);
182 CharUpperA(resource_name); // Uppercases inplace
183 }
184
185 HRSRC resource_handle =
186 FindResourceA(NULL, resource_name != NULL ? resource_name : name,
187 MAKEINTRESOURCEA(10) /* RT_RCDATA */);
188
189 if (resource_handle) {
190 HGLOBAL global_resource_handle = LoadResource(NULL, resource_handle);
191
192 if (global_resource_handle) {
193 if (size != NULL) {
194 *size = SizeofResource(NULL, resource_handle);
195 }
196
197 ptr = LockResource(global_resource_handle);
198 }
199 }
200
201 free(resource_name);
202
203 return ptr;
204 #else
205 return NULL;
206 #endif
207 }
208
209 #endif // POSTJECT_API_H_
210