Coverage Report

Created: 2025-08-28 09:57

/src/node/deps/postject/postject-api.h
Line
Count
Source (jump to first uncovered line)
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
33
0
inline void postject_options_init(struct postject_options* options) {
34
0
  options->elf_section_name = NULL;
35
0
  options->macho_framework_name = NULL;
36
0
  options->macho_section_name = NULL;
37
0
  options->macho_segment_name = NULL;
38
0
  options->pe_resource_name = NULL;
39
0
}
40
41
488k
static inline bool postject_has_resource() {
42
488k
  static const volatile char* sentinel = POSTJECT_SENTINEL_FUSE ":0";
43
488k
  return sentinel[sizeof(POSTJECT_SENTINEL_FUSE)] == '1';
44
488k
}
45
46
#if defined(__linux__)
47
static int postject__dl_iterate_phdr_callback(struct dl_phdr_info* info,
48
                                              size_t size,
49
0
                                              void* data) {
50
  // Snag the dl_phdr_info struct for the main program, then stop iterating
51
0
  *((struct dl_phdr_info*)data) = *info;
52
0
  return 1;
53
0
}
54
#endif
55
56
static const void* postject_find_resource(
57
    const char* name,
58
    size_t* size,
59
0
    const struct postject_options* options) {
60
  // Always zero out the size pointer to start
61
0
  if (size != NULL) {
62
0
    *size = 0;
63
0
  }
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
0
  if (options != NULL && options->elf_section_name != NULL) {
123
0
    name = options->elf_section_name;
124
0
  }
125
126
0
  struct dl_phdr_info main_program_info;
127
0
  dl_iterate_phdr(postject__dl_iterate_phdr_callback, &main_program_info);
128
129
0
  uintptr_t p = (uintptr_t)main_program_info.dlpi_phdr;
130
0
  size_t n = main_program_info.dlpi_phnum;
131
0
  uintptr_t base_addr = main_program_info.dlpi_addr;
132
133
  // iterate program header
134
0
  for (; n > 0; n--, p += sizeof(ElfW(Phdr))) {
135
0
    ElfW(Phdr)* phdr = (ElfW(Phdr)*)p;
136
137
    // skip everything but notes
138
0
    if (phdr->p_type != PT_NOTE) {
139
0
      continue;
140
0
    }
141
142
    // note segment starts at base address + segment virtual address
143
0
    uintptr_t pos = (base_addr + phdr->p_vaddr);
144
0
    uintptr_t end = (pos + phdr->p_memsz);
145
146
    // iterate through segment until we reach the end
147
0
    while (pos < end) {
148
0
      if (pos + sizeof(ElfW(Nhdr)) > end) {
149
0
        break;  // invalid
150
0
      }
151
152
0
      ElfW(Nhdr)* note = (ElfW(Nhdr)*)(uintptr_t)pos;
153
0
      if (note->n_namesz != 0 && note->n_descsz != 0 &&
154
0
          strncmp((char*)(pos + sizeof(ElfW(Nhdr))), (char*)name,
155
0
                  sizeof(name)) == 0) {
156
0
        *size = note->n_descsz;
157
        // advance past note header and aligned name
158
        // to get to description data
159
0
        return (void*)((uintptr_t)note + sizeof(ElfW(Nhdr)) +
160
0
                       roundup(note->n_namesz, 4));
161
0
      }
162
163
0
      pos += (sizeof(ElfW(Nhdr)) + roundup(note->n_namesz, 4) +
164
0
              roundup(note->n_descsz, 4));
165
0
    }
166
0
  }
167
0
  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
0
}
208
209
#endif  // POSTJECT_API_H_