• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                                    &section_size);
95   } else {
96     ptr = getsectdata(segment_name, section_name != NULL ? section_name : name,
97                       &section_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