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
25struct 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
33inline 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
41static 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__)
47static 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
56static 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