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 §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