The Pedigree Project 0.1
ModuleImage.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/TargetInfo.h"
3#include "pedigree/kernel/linker/ModuleImage.h"
4#include "pedigree/kernel/utilities/utility.h"
5
6namespace {
7bool range(size_t offset, size_t length, size_t limit) {
8 return offset <= limit && length <= limit - offset;
9}
10
11bool overlap(uintptr_t first, size_t firstBytes, uintptr_t second, size_t secondBytes) {
12 return first <= second ? second - first < firstBytes : first - second < secondBytes;
13}
14
15constexpr uint32_t Relative = 8;
16constexpr uint32_t Absolute = 1;
17constexpr uint32_t GlobalData = 6;
18constexpr uint32_t JumpSlot = 7;
19} // namespace
20
21ModuleImage::ModuleImage() {
22 ByteSet(this, 0, sizeof(*this));
23}
24
25bool ModuleImage::section(size_t index, Section& result) const {
26 if (index >= sectionCount) {
27 return false;
28 }
29 MemoryCopy(&result, bytes + sectionOffset + index * sizeof(result), sizeof(result));
30 return true;
31}
32
33bool ModuleImage::contains(uintptr_t address, size_t length, size_t flags) const {
34 for (size_t i = 0; i < segmentCount; ++i) {
35 const auto& segment = segments[i];
36 if ((segment.flags & flags) == flags && address >= segment.vaddr &&
37 range(address - segment.vaddr, length, segment.memsz)) {
38 return true;
39 }
40 }
41 return false;
42}
43
44bool ModuleImage::fileOffset(uintptr_t address, size_t length, size_t& offset) const {
45 for (size_t i = 0; i < segmentCount; ++i) {
46 const auto& segment = segments[i];
47 if (address >= segment.vaddr && range(address - segment.vaddr, length, segment.filesz)) {
48 offset = segment.offset + address - segment.vaddr;
49 return range(offset, length, byteCount);
50 }
51 }
52 return false;
53}
54
55bool ModuleImage::string(size_t tableOffset, size_t tableBytes, size_t offset, const char*& result,
56 size_t maximum) const {
57 if (offset >= tableBytes || !range(tableOffset, tableBytes, byteCount)) {
58 return false;
59 }
60 result = reinterpret_cast<const char*>(bytes + tableOffset + offset);
61 const size_t available = pedigree_std::min(tableBytes - offset, maximum);
62 return BoundedStringLength(result, available) < available;
63}
64
65bool ModuleImage::symbol(size_t index, Symbol& result, bool dynamic) const {
66 const size_t offset = dynamic ? symbols : fullSymbols;
67 const size_t length = dynamic ? symbolBytes : fullSymbolBytes;
68 if (index >= length / sizeof(result)) {
69 return false;
70 }
71 MemoryCopy(&result, bytes + offset + index * sizeof(result), sizeof(result));
72 return true;
73}
74
75const char* ModuleImage::symbolName(const Symbol& value, bool dynamic) const {
76 const char* result = nullptr;
77 return string(dynamic ? strings : fullStrings, dynamic ? stringBytes : fullStringBytes,
78 value.name, result, SymbolNameBytes)
79 ? result
80 : nullptr;
81}
82
83bool ModuleImage::findSymbol(const char* wanted, Symbol& result, bool dynamic) const {
84 bool found = false;
85 const size_t count = (dynamic ? symbolBytes : fullSymbolBytes) / sizeof(Symbol);
86 for (size_t i = 0; i < count; ++i) {
87 Symbol candidate;
88 if (!symbol(i, candidate, dynamic)) {
89 return false;
90 }
91 const char* name = symbolName(candidate, dynamic);
92 if (candidate.shndx && name && !StringCompare(name, wanted)) {
93 if (found) {
94 return false;
95 }
96 result = candidate;
97 found = true;
98 }
99 }
100 return found;
101}
102
103bool ModuleImage::relocation(size_t index, Relocation& result) const {
104 if (index >= relocationCount) {
105 return false;
106 }
107 MemoryCopy(&result, bytes + relocations + index * sizeof(result), sizeof(result));
108 return true;
109}
110
111bool ModuleImage::localPointer(uintptr_t address, uintptr_t& target) const {
112 size_t offset = 0;
113 if (!fileOffset(address, sizeof(uintptr_t), offset)) {
114 return false;
115 }
116 MemoryCopy(&target, bytes + offset, sizeof(target));
117 for (size_t i = 0; i < relocationCount; ++i) {
118 Relocation value;
119 relocation(i, value);
120 if (value.offset != address) {
121 continue;
122 }
123 if (R_TYPE(value.info) == Relative) {
124 target = value.addend;
125 return true;
126 }
127 Symbol local;
128 if (R_TYPE(value.info) != Absolute || !symbol(R_SYM(value.info), local) || !local.shndx ||
129 value.addend < 0 || !range(local.value, value.addend, MaximumMappedBytes)) {
130 return false;
131 }
132 target = local.value + value.addend;
133 return true;
134 }
135 // Unrelocated metadata may only express a sentinel, never an address.
136 return target == 0 || target == ~uintptr_t{0};
137}
138
139ModuleImage::Result ModuleImage::preflight(const uint8_t* image, size_t length) {
140 ByteSet(this, 0, sizeof(*this));
141#if !BITS_64 || (!X64 && !HOSTED)
142 return Result::Unsupported;
143#endif
144 if (length > MaximumImageBytes) {
145 return Result::TooLarge;
146 }
148 const auto headerResult = Elf::validateExecutableHeader(image, length, length, metadata);
149 if (headerResult != Elf::ExecutableValidationResult::Valid) {
150 return headerResult == Elf::ExecutableValidationResult::Malformed ? Result::Malformed
151 : Result::Unsupported;
152 }
153 Header header;
154 MemoryCopy(&header, image, sizeof(header));
155 if (header.type != ET_DYN || header.entry || header.flags || header.phnum > MaximumSegments ||
156 !header.shnum || header.shnum > MaximumSections || header.shentsize != sizeof(Section) ||
157 !header.shstrndx || header.shstrndx >= header.shnum) {
158 return Result::Unsupported;
159 }
160 if (!range(header.shoff, header.shnum * sizeof(Section), length)) {
161 return Result::Malformed;
162 }
163 bytes = image;
164 byteCount = length;
165 sectionOffset = header.shoff;
166 sectionCount = header.shnum;
167 bool executable = false;
168 for (size_t i = 0; i < header.phnum; ++i) {
169 Segment segment;
170 MemoryCopy(&segment, image + header.phoff + i * sizeof(segment), sizeof(segment));
171 if (segment.type == PT_LOAD) {
172 if (!(segment.flags & PF_R) || (segment.flags & ~(PF_R | PF_W | PF_X)) ||
173 ((segment.flags & PF_W) && (segment.flags & PF_X))) {
174 return Result::Unsupported;
175 }
176 segments[segmentCount++] = segment;
177 if ((segment.flags & PF_X) && segment.memsz && !executable) {
178 // The executable validator's entry coverage becomes module code coverage.
179 // The original module e_entry remains required to be zero above.
180 metadata.entryPoint = segment.vaddr;
181 executable = true;
182 }
183 } else if (segment.type == PT_DYNAMIC) {
184 if (dynamicBytes || !segment.filesz || segment.filesz != segment.memsz ||
185 segment.filesz % sizeof(Dynamic)) {
186 return Result::Malformed;
187 }
188 dynamicOffset = segment.offset;
189 dynamicBytes = segment.filesz;
190 } else if (segment.type != PT_NULL && segment.type != PT_NOTE && segment.type != PT_PHDR &&
191 !(segment.type == 0x6474e551 && !(segment.flags & PF_X))) {
192 return Result::Unsupported;
193 }
194 }
195 const auto segmentsResult = Elf::validateExecutableProgramHeaders(
196 image + header.phoff, metadata.programHeaderSize, length, metadata);
197 if (!executable || segmentsResult != Elf::ExecutableValidationResult::Valid || !dynamicBytes ||
198 metadata.hasInterpreter) {
199 return Result::Malformed;
200 }
201 if (metadata.loadEnd > MaximumMappedBytes) {
202 return Result::TooLarge;
203 }
204 mappedBytes = metadata.loadEnd;
205 Result result = validateSections(header);
206 if (result == Result::Valid)
207 result = validateDynamic();
208 if (result == Result::Valid)
209 result = validateRelocations();
210 if (result == Result::Valid)
211 result = validateMetadata();
212 return result;
213}
214
215ModuleImage::Result ModuleImage::validateSections(const Header& header) {
216 Section names;
217 section(header.shstrndx, names);
218 if (names.type != SHT_STRTAB || !names.size || !range(names.offset, names.size, byteCount)) {
219 return Result::Malformed;
220 }
221 sectionStrings = names.offset;
222 sectionStringBytes = names.size;
223 size_t dynamicSectionCount = 0;
224 size_t dynamicSymbolIndex = 0;
225 for (size_t i = 0; i < sectionCount; ++i) {
226 Section value;
227 section(i, value);
228 const char* sectionName = nullptr;
229 if (!string(sectionStrings, sectionStringBytes, value.name, sectionName, SymbolNameBytes) ||
230 (value.type != SHT_NOBITS && !range(value.offset, value.size, byteCount)) ||
231 (value.addralign && (value.addralign & (value.addralign - 1)))) {
232 return Result::Malformed;
233 }
234 if ((value.flags & (0x400 | 0x800)) || value.type == SHT_REL ||
235 value.type == SHT_PREINIT_ARRAY) {
236 return Result::Unsupported;
237 }
238 if ((value.flags & SHF_ALLOC) && value.size) {
239 size_t offset = 0;
240 if (!contains(value.addr, value.size, PF_R | ((value.flags & SHF_EXECINSTR) ? PF_X : 0)) ||
241 (value.type != SHT_NOBITS &&
242 (!fileOffset(value.addr, value.size, offset) || offset != value.offset))) {
243 return Result::Malformed;
244 }
245 }
246 if (value.type == SHT_DYNAMIC) {
247 if (++dynamicSectionCount != 1 || value.offset != dynamicOffset ||
248 value.size != dynamicBytes || value.entsize != sizeof(Dynamic) ||
249 !(value.flags & SHF_ALLOC)) {
250 return Result::Malformed;
251 }
252 } else if (value.type == SHT_SYMTAB || value.type == SHT_DYNSYM) {
253 Section stringsSection;
254 if (!value.size || value.size % sizeof(Symbol) || value.entsize != sizeof(Symbol) ||
255 value.size / sizeof(Symbol) > MaximumSymbols || !section(value.link, stringsSection) ||
256 stringsSection.type != SHT_STRTAB || !stringsSection.size ||
257 !range(stringsSection.offset, stringsSection.size, byteCount)) {
258 return Result::Malformed;
259 }
260 if (value.type == SHT_DYNSYM) {
261 if (symbolBytes || !(value.flags & SHF_ALLOC))
262 return Result::Unsupported;
263 symbols = value.offset;
264 symbolBytes = value.size;
265 strings = stringsSection.offset;
266 stringBytes = stringsSection.size;
267 symbolAddress = value.addr;
268 stringAddress = stringsSection.addr;
269 dynamicSymbolIndex = i;
270 } else {
271 if (fullSymbolBytes)
272 return Result::Unsupported;
273 fullSymbols = value.offset;
274 fullSymbolBytes = value.size;
275 fullStrings = stringsSection.offset;
276 fullStringBytes = stringsSection.size;
277 }
278 } else if (value.type == SHT_RELA && (value.flags & SHF_ALLOC)) {
279 if (relocationBytes || !value.size || value.entsize != sizeof(Relocation) ||
280 value.size % sizeof(Relocation) || value.size / sizeof(Relocation) > MaximumRelocations) {
281 return Result::Unsupported;
282 }
283 relocations = value.offset;
284 relocationBytes = value.size;
285 relocationAddress = value.addr;
286 }
287 }
288 if (dynamicSectionCount != 1 || !symbolBytes || !fullSymbolBytes) {
289 return Result::Malformed;
290 }
291 symbolCount = symbolBytes / sizeof(Symbol);
292 relocationCount = relocationBytes / sizeof(Relocation);
293 for (size_t i = 0; i < sectionCount; ++i) {
294 Section value;
295 section(i, value);
296 if (value.type == SHT_RELA && (value.flags & SHF_ALLOC) &&
297 (value.link != dynamicSymbolIndex || value.info)) {
298 return Result::Unsupported;
299 }
300 }
301 for (size_t table = 0; table < 2; ++table) {
302 const bool dynamic = table == 0;
303 const size_t count = (dynamic ? symbolBytes : fullSymbolBytes) / sizeof(Symbol);
304 for (size_t i = 0; i < count; ++i) {
305 Symbol value;
306 symbol(i, value, dynamic);
307 if (!i &&
308 (value.name || value.info || value.other || value.shndx || value.value || value.size)) {
309 return Result::Malformed;
310 }
311 if (!symbolName(value, dynamic) || ST_BIND(value.info) > STB_WEAK ||
312 ST_TYPE(value.info) > STT_FILE || (value.other & ~3U)) {
313 return Result::Unsupported;
314 }
315 if (!value.shndx) {
316 if (value.value || (!i && (value.name || value.info || value.other || value.size))) {
317 return Result::Malformed;
318 }
319 } else if (value.shndx != 0xfff1) {
320 Section owner;
321 if (!section(value.shndx, owner))
322 return Result::Malformed;
323 if (dynamic && !(owner.flags & SHF_ALLOC))
324 return Result::Unsupported;
325 if ((owner.flags & SHF_ALLOC) &&
326 (!contains(value.value, value.size) || value.value < owner.addr ||
327 !range(value.value - owner.addr, value.size, owner.size))) {
328 return Result::Malformed;
329 }
330 if (ST_TYPE(value.info) == STT_FUNC && (owner.flags & SHF_ALLOC) &&
331 !contains(value.value, value.size ? value.size : 1, PF_R | PF_X)) {
332 return Result::Malformed;
333 }
334 } else if (dynamic || ST_TYPE(value.info) != STT_FILE) {
335 return Result::Unsupported;
336 }
337 }
338 }
339 return Result::Valid;
340}
341
342ModuleImage::Result ModuleImage::validateDynamic() {
343 uintptr_t rela = 0, jump = 0, sym = 0, str = 0;
344 size_t relaBytes = 0, jumpBytes = 0, stringsBytes = 0;
345 size_t relaEntry = 0, symbolEntry = 0, pltType = 0;
346 uint64_t seen = 0;
347 bool terminated = false;
348 for (size_t i = 0; i < dynamicBytes / sizeof(Dynamic); ++i) {
349 Dynamic value;
350 MemoryCopy(&value, bytes + dynamicOffset + i * sizeof(value), sizeof(value));
351 if (terminated) {
352 if (value.tag || value.un.val)
353 return Result::Malformed;
354 continue;
355 }
356 if (value.tag == DT_NULL) {
357 terminated = true;
358 continue;
359 }
360 if (value.tag >= 0 && value.tag < 64 && value.tag != DT_NEEDED) {
361 const uint64_t bit = uint64_t{1} << value.tag;
362 if (seen & bit)
363 return Result::Malformed;
364 seen |= bit;
365 }
366 switch (value.tag) {
367 case DT_NEEDED: {
368 const char* needed = nullptr;
369 if (!string(strings, stringBytes, value.un.val, needed, SymbolNameBytes) ||
370 StringCompare(needed, "libkernel_shared.so"))
371 return Result::Unsupported;
372 break;
373 }
374 case DT_SYMTAB:
375 sym = value.un.ptr;
376 break;
377 case DT_STRTAB:
378 str = value.un.ptr;
379 break;
380 case DT_STRSZ:
381 stringsBytes = value.un.val;
382 break;
383 case DT_SYMENT:
384 symbolEntry = value.un.val;
385 break;
386 case DT_RELA:
387 rela = value.un.ptr;
388 break;
389 case DT_RELASZ:
390 relaBytes = value.un.val;
391 break;
392 case DT_RELAENT:
393 relaEntry = value.un.val;
394 break;
395 case DT_JMPREL:
396 jump = value.un.ptr;
397 break;
398 case DT_PLTRELSZ:
399 jumpBytes = value.un.val;
400 break;
401 case DT_PLTREL:
402 pltType = value.un.val;
403 break;
404 case DT_INIT_ARRAY:
405 constructorAddress = value.un.ptr;
406 break;
407 case DT_INIT_ARRAYSZ:
408 constructorBytes = value.un.val;
409 break;
410 case DT_FINI_ARRAY:
411 destructorAddress = value.un.ptr;
412 break;
413 case DT_FINI_ARRAYSZ:
414 destructorBytes = value.un.val;
415 break;
416 case DT_PLTGOT:
417 case DT_HASH:
418 case 0x6ffffef5: {
419 size_t unused = 0;
420 if (!fileOffset(value.un.ptr, sizeof(uintptr_t), unused))
421 return Result::Malformed;
422 break;
423 }
424 case DT_BIND_NOW:
425 if (value.un.val)
426 return Result::Unsupported;
427 break;
428 case DT_FLAGS:
429 if (value.un.val & ~uint64_t{8})
430 return Result::Unsupported;
431 break;
432 case 0x6ffffffb:
433 if (value.un.val & ~uint64_t{1})
434 return Result::Unsupported;
435 break;
436 case 0x6ffffff9:
437 if (value.un.val > relocationCount)
438 return Result::Malformed;
439 break;
440 default:
441 return Result::Unsupported;
442 }
443 }
444 if (!terminated || sym != symbolAddress || str != stringAddress || stringsBytes != stringBytes ||
445 symbolEntry != sizeof(Symbol) || relaBytes > relocationBytes ||
446 jumpBytes != relocationBytes - relaBytes ||
447 (relocationBytes && relaEntry != sizeof(Relocation)) ||
448 (relaBytes && rela != relocationAddress) ||
449 (jumpBytes && (jump != relocationAddress + relaBytes || pltType != DT_RELA))) {
450 return Result::Malformed;
451 }
452 return Result::Valid;
453}
454
455ModuleImage::Result ModuleImage::validateRelocations() {
456 uintptr_t previous = 0;
457 for (size_t i = 0; i < relocationCount; ++i) {
458 Relocation value;
459 relocation(i, value);
460 const size_t kind = R_TYPE(value.info);
461 if ((kind != Relative && kind != Absolute && kind != GlobalData && kind != JumpSlot) ||
462 !contains(value.offset, sizeof(uintptr_t)) ||
463 contains(value.offset, sizeof(uintptr_t), PF_X) ||
464 (i && (value.offset < previous || value.offset - previous < sizeof(uintptr_t)))) {
465 return Result::Unsupported;
466 }
467 previous = value.offset;
468 if (overlap(value.offset, sizeof(uintptr_t), symbolAddress, symbolBytes) ||
469 overlap(value.offset, sizeof(uintptr_t), stringAddress, stringBytes) ||
470 overlap(value.offset, sizeof(uintptr_t), relocationAddress, relocationBytes)) {
471 return Result::Malformed;
472 }
473 if (kind == Relative) {
474 if (R_SYM(value.info) || value.addend < 0 || !contains(value.addend, 1)) {
475 return Result::Malformed;
476 }
477 } else {
478 Symbol target;
479 if (!symbol(R_SYM(value.info), target) || !R_SYM(value.info) ||
480 ST_TYPE(target.info) > STT_FUNC || (!target.shndx && ST_BIND(target.info) == STB_LOCAL) ||
481 ((kind == JumpSlot || kind == GlobalData) && value.addend)) {
482 return Result::Malformed;
483 }
484 if (target.shndx &&
485 (value.addend < 0 || !range(target.value, value.addend, MaximumMappedBytes) ||
486 !contains(target.value + value.addend, 1))) {
487 return Result::Malformed;
488 }
489 }
490 }
491 return Result::Valid;
492}
493
494bool ModuleImage::namedObject(const char* wanted, size_t size, Symbol& result) const {
495 Section owner;
496 Symbol exported;
497 if (!findSymbol(wanted, exported, true) || !findSymbol(wanted, result) ||
498 exported.value != result.value || exported.size != result.size ||
499 exported.shndx != result.shndx || exported.info != result.info || result.size != size ||
500 ST_TYPE(result.info) != STT_OBJECT || !section(result.shndx, owner))
501 return false;
502 const char* ownerName = nullptr;
503 return string(sectionStrings, sectionStringBytes, owner.name, ownerName, SymbolNameBytes) &&
504 !StringCompare(ownerName, ".modinfo") && contains(result.value, size);
505}
506
507bool ModuleImage::dependenciesAt(const char* wanted, char names[][NameBytes], size_t& count,
508 bool optional) const {
509 Symbol list;
510 if (!findSymbol(wanted, list))
511 return optional;
512 if (list.size < sizeof(uintptr_t) || list.size % sizeof(uintptr_t) ||
513 list.size / sizeof(uintptr_t) > MaximumDependencies + 1 ||
514 !namedObject(wanted, list.size, list))
515 return false;
516 for (size_t i = 0; i < list.size / sizeof(uintptr_t); ++i) {
517 uintptr_t target = 0;
518 if (!localPointer(list.value + i * sizeof(uintptr_t), target))
519 return false;
520 if (!target)
521 return i + 1 == list.size / sizeof(uintptr_t);
522 if (count == MaximumDependencies)
523 return false;
524 size_t offset = 0;
525 if (!fileOffset(target, 1, offset))
526 return false;
527 const char* dependency = nullptr;
528 if (!mappedString(target, dependency, NameBytes) || !*dependency)
529 return false;
530 const size_t length = StringLength(dependency);
531 for (size_t j = 0; j < count; ++j) {
532 if (!StringCompare(names[j], dependency))
533 return false;
534 }
535 MemoryCopy(names[count++], dependency, length + 1);
536 }
537 return false;
538}
539
540bool ModuleImage::lifecycle(const char* startName, const char* endName, uintptr_t* targets,
541 size_t& count) const {
542 Symbol first, last;
543 if (!findSymbol(startName, first) || !findSymbol(endName, last) || last.value < first.value ||
544 (last.value - first.value) % sizeof(uintptr_t) ||
545 (last.value - first.value) / sizeof(uintptr_t) > MaximumLifecycleFunctions ||
546 !contains(first.value, last.value - first.value))
547 return false;
548 const bool constructor = !StringCompare(startName, "start_ctors");
549 const uintptr_t expected = constructor ? constructorAddress : destructorAddress;
550 const size_t expectedBytes = constructor ? constructorBytes : destructorBytes;
551 if (expectedBytes && (expected != first.value || expectedBytes != last.value - first.value)) {
552 return false;
553 }
554 for (uintptr_t at = first.value; at < last.value; at += sizeof(uintptr_t)) {
555 uintptr_t target = 0;
556 if (!localPointer(at, target))
557 return false;
558 if (!target)
559 break;
560 if (target == ~uintptr_t{0})
561 continue;
562 if (!contains(target, 1, PF_R | PF_X))
563 return false;
564 targets[count++] = target;
565 }
566 return true;
567}
568
569ModuleImage::Result ModuleImage::validateMetadata() {
570 Symbol nameObject, entryObject, exitObject, unloadable, runtimeUnloadable, optionalFunction;
571 if (!namedObject("g_pModuleName", sizeof(uintptr_t), nameObject) ||
572 !namedObject("g_pModuleEntry", sizeof(uintptr_t), entryObject) ||
573 !namedObject("g_pModuleExit", sizeof(uintptr_t), exitObject) ||
574 !namedObject("g_bModuleUnloadable", 1, unloadable) ||
575 !namedObject("g_bModuleRuntimeUnloadable", 1, runtimeUnloadable) ||
576 findSymbol("__add_optional_deps", optionalFunction))
577 return Result::Unsupported;
578 size_t offset = 0;
579 if (!fileOffset(unloadable.value, 1, offset) || bytes[offset] != 1 ||
580 !fileOffset(runtimeUnloadable.value, 1, offset) || bytes[offset] != 1) {
581 return Result::Unsupported;
582 }
583 uintptr_t nameAddress = 0;
584 const char* moduleName = nullptr;
585 if (!localPointer(nameObject.value, nameAddress) || !fileOffset(nameAddress, 1, offset) ||
586 !mappedString(nameAddress, moduleName, NameBytes) || !*moduleName ||
587 !StringCompare(moduleName, "init") || !localPointer(entryObject.value, entry) || !entry ||
588 !contains(entry, 1, PF_R | PF_X) || !localPointer(exitObject.value, exit) || !exit ||
589 !contains(exit, 1, PF_R | PF_X)) {
590 return Result::Malformed;
591 }
592 const size_t length = StringLength(moduleName);
593 for (size_t i = 0; i < length; ++i) {
594 const char c = moduleName[i];
595 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' ||
596 c == '_'))
597 return Result::Unsupported;
598 }
599 MemoryCopy(name, moduleName, length + 1);
600 if (!dependenciesAt("g_pDepends", dependencies, dependencyCount, false) ||
601 !dependenciesAt("g_pOptionalDepends", optionalDependencies, optionalDependencyCount, true) ||
602 !lifecycle("start_ctors", "end_ctors", constructors, constructorCount) ||
603 !lifecycle("start_dtors", "end_dtors", destructors, destructorCount)) {
604 return Result::Malformed;
605 }
606 return Result::Valid;
607}
608
609bool ModuleImage::mappedString(uintptr_t address, const char*& result, size_t maximum) const {
610 for (size_t i = 0; i < segmentCount; ++i) {
611 const auto& segment = segments[i];
612 if (address >= segment.vaddr && address - segment.vaddr < segment.filesz) {
613 return string(segment.offset, segment.filesz, address - segment.vaddr, result, maximum);
614 }
615 }
616 return false;
617}
618
619bool ModuleImage::exportedSymbol(size_t index, Symbol& result) const {
620 Section owner;
621 return symbol(index, result) && result.shndx && section(result.shndx, owner) &&
622 (owner.flags & SHF_ALLOC) &&
623 (ST_BIND(result.info) == STB_GLOBAL || ST_BIND(result.info) == STB_WEAK) &&
624 ST_TYPE(result.info) <= STT_FUNC && (result.other == 0 || result.other == 3) &&
625 contains(result.value, result.size);
626}
627
628bool ModuleImage::validateMaterialized(uintptr_t base) const {
629 // Metadata is frozen in the plan. Verify relocation did not change the ABI
630 // which the module itself observes before calling any of its code.
631 const char* objects[] = {"g_pModuleName", "g_pModuleEntry", "g_pModuleExit", "g_pDepends",
632 "g_pOptionalDepends"};
633 for (size_t i = 0; i < sizeof(objects) / sizeof(objects[0]); ++i) {
634 Symbol value;
635 if (!findSymbol(objects[i], value)) {
636 if (i == 4)
637 continue;
638 return false;
639 }
640 for (size_t offset = 0; offset < value.size; offset += sizeof(uintptr_t)) {
641 uintptr_t expected = 0, actual = 0;
642 if (!localPointer(value.value + offset, expected))
643 return false;
644 MemoryCopy(&actual, reinterpret_cast<void*>(base + value.value + offset), sizeof(actual));
645 if (actual != (expected ? base + expected : 0))
646 return false;
647 if (i == 0 || (i >= 3 && expected)) {
648 const char* original = nullptr;
649 if (!mappedString(expected, original, NameBytes) ||
650 MemoryCompare(original, reinterpret_cast<void*>(base + expected),
651 StringLength(original) + 1))
652 return false;
653 }
654 }
655 }
656 const char* starts[] = {"start_ctors", "start_dtors"};
657 const char* ends[] = {"end_ctors", "end_dtors"};
658 for (size_t i = 0; i < 2; ++i) {
659 Symbol first, last;
660 if (!findSymbol(starts[i], first) || !findSymbol(ends[i], last))
661 return false;
662 for (uintptr_t at = first.value; at < last.value; at += sizeof(uintptr_t)) {
663 uintptr_t expected = 0, actual = 0;
664 if (!localPointer(at, expected))
665 return false;
666 MemoryCopy(&actual, reinterpret_cast<void*>(base + at), sizeof(actual));
667 if (actual != ((!expected || expected == ~uintptr_t{0}) ? expected : base + expected))
668 return false;
669 }
670 }
671 const char* flags[] = {"g_bModuleUnloadable", "g_bModuleRuntimeUnloadable"};
672 for (const char* flag : flags) {
673 Symbol value;
674 if (!findSymbol(flag, value) || *reinterpret_cast<const uint8_t*>(base + value.value) != 1)
675 return false;
676 }
677 return true;
678}
static ExecutableValidationResult validateExecutableProgramHeaders(const uint8_t *pBuffer, size_t length, size_t fileSize, ExecutableMetadata &metadata)
static ExecutableValidationResult validateExecutableHeader(const uint8_t *pBuffer, size_t length, size_t fileSize, ExecutableMetadata &metadata)