2#include "pedigree/kernel/TargetInfo.h"
3#include "pedigree/kernel/linker/ModuleImage.h"
4#include "pedigree/kernel/utilities/utility.h"
7bool range(
size_t offset,
size_t length,
size_t limit) {
8 return offset <= limit && length <= limit - offset;
11bool overlap(uintptr_t first,
size_t firstBytes, uintptr_t second,
size_t secondBytes) {
12 return first <= second ? second - first < firstBytes : first - second < secondBytes;
15constexpr uint32_t Relative = 8;
16constexpr uint32_t Absolute = 1;
17constexpr uint32_t GlobalData = 6;
18constexpr uint32_t JumpSlot = 7;
21ModuleImage::ModuleImage() {
22 ByteSet(
this, 0,
sizeof(*
this));
25bool ModuleImage::section(
size_t index, Section& result)
const {
26 if (index >= sectionCount) {
29 MemoryCopy(&result, bytes + sectionOffset + index *
sizeof(result),
sizeof(result));
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)) {
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);
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)) {
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;
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)) {
71 MemoryCopy(&result, bytes + offset + index *
sizeof(result),
sizeof(result));
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)
83bool ModuleImage::findSymbol(
const char* wanted, Symbol& result,
bool dynamic)
const {
85 const size_t count = (dynamic ? symbolBytes : fullSymbolBytes) /
sizeof(Symbol);
86 for (
size_t i = 0; i < count; ++i) {
88 if (!
symbol(i, candidate, dynamic)) {
91 const char* name = symbolName(candidate, dynamic);
92 if (candidate.shndx && name && !StringCompare(name, wanted)) {
103bool ModuleImage::relocation(
size_t index, Relocation& result)
const {
104 if (index >= relocationCount) {
107 MemoryCopy(&result, bytes + relocations + index *
sizeof(result),
sizeof(result));
111bool ModuleImage::localPointer(uintptr_t address, uintptr_t& target)
const {
113 if (!fileOffset(address,
sizeof(uintptr_t), offset)) {
116 MemoryCopy(&target, bytes + offset,
sizeof(target));
117 for (
size_t i = 0; i < relocationCount; ++i) {
119 relocation(i, value);
120 if (value.offset != address) {
123 if (R_TYPE(value.info) == Relative) {
124 target = value.addend;
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)) {
132 target = local.value + value.addend;
136 return target == 0 || target == ~uintptr_t{0};
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;
144 if (length > MaximumImageBytes) {
145 return Result::TooLarge;
149 if (headerResult != Elf::ExecutableValidationResult::Valid) {
150 return headerResult == Elf::ExecutableValidationResult::Malformed ? Result::Malformed
151 : Result::Unsupported;
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;
160 if (!range(header.shoff, header.shnum *
sizeof(Section), length)) {
161 return Result::Malformed;
165 sectionOffset = header.shoff;
166 sectionCount = header.shnum;
167 bool executable =
false;
168 for (
size_t i = 0; i < header.phnum; ++i) {
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;
176 segments[segmentCount++] = segment;
177 if ((segment.flags & PF_X) && segment.memsz && !executable) {
180 metadata.entryPoint = segment.vaddr;
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;
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;
196 image + header.phoff, metadata.programHeaderSize, length, metadata);
197 if (!executable || segmentsResult != Elf::ExecutableValidationResult::Valid || !dynamicBytes ||
198 metadata.hasInterpreter) {
199 return Result::Malformed;
201 if (metadata.loadEnd > MaximumMappedBytes) {
202 return Result::TooLarge;
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();
215ModuleImage::Result ModuleImage::validateSections(
const Header& header) {
217 section(header.shstrndx, names);
218 if (names.type != SHT_STRTAB || !names.size || !range(names.offset, names.size, byteCount)) {
219 return Result::Malformed;
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) {
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;
234 if ((value.flags & (0x400 | 0x800)) || value.type == SHT_REL ||
235 value.type == SHT_PREINIT_ARRAY) {
236 return Result::Unsupported;
238 if ((value.flags & SHF_ALLOC) && value.size) {
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;
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;
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;
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;
272 return Result::Unsupported;
273 fullSymbols = value.offset;
274 fullSymbolBytes = value.size;
275 fullStrings = stringsSection.offset;
276 fullStringBytes = stringsSection.size;
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;
283 relocations = value.offset;
284 relocationBytes = value.size;
285 relocationAddress = value.addr;
288 if (dynamicSectionCount != 1 || !symbolBytes || !fullSymbolBytes) {
289 return Result::Malformed;
291 symbolCount = symbolBytes /
sizeof(Symbol);
292 relocationCount = relocationBytes /
sizeof(Relocation);
293 for (
size_t i = 0; i < sectionCount; ++i) {
296 if (value.type == SHT_RELA && (value.flags & SHF_ALLOC) &&
297 (value.link != dynamicSymbolIndex || value.info)) {
298 return Result::Unsupported;
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) {
306 symbol(i, value, dynamic);
308 (value.name || value.info || value.other || value.shndx || value.value || value.size)) {
309 return Result::Malformed;
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;
316 if (value.value || (!i && (value.name || value.info || value.other || value.size))) {
317 return Result::Malformed;
319 }
else if (value.shndx != 0xfff1) {
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;
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;
334 }
else if (dynamic || ST_TYPE(value.info) != STT_FILE) {
335 return Result::Unsupported;
339 return Result::Valid;
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;
347 bool terminated =
false;
348 for (
size_t i = 0; i < dynamicBytes /
sizeof(Dynamic); ++i) {
350 MemoryCopy(&value, bytes + dynamicOffset + i *
sizeof(value),
sizeof(value));
352 if (value.tag || value.un.val)
353 return Result::Malformed;
356 if (value.tag == DT_NULL) {
360 if (value.tag >= 0 && value.tag < 64 && value.tag != DT_NEEDED) {
361 const uint64_t bit = uint64_t{1} << value.tag;
363 return Result::Malformed;
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;
381 stringsBytes = value.un.val;
384 symbolEntry = value.un.val;
390 relaBytes = value.un.val;
393 relaEntry = value.un.val;
399 jumpBytes = value.un.val;
402 pltType = value.un.val;
405 constructorAddress = value.un.ptr;
407 case DT_INIT_ARRAYSZ:
408 constructorBytes = value.un.val;
411 destructorAddress = value.un.ptr;
413 case DT_FINI_ARRAYSZ:
414 destructorBytes = value.un.val;
420 if (!fileOffset(value.un.ptr,
sizeof(uintptr_t), unused))
421 return Result::Malformed;
426 return Result::Unsupported;
429 if (value.un.val & ~uint64_t{8})
430 return Result::Unsupported;
433 if (value.un.val & ~uint64_t{1})
434 return Result::Unsupported;
437 if (value.un.val > relocationCount)
438 return Result::Malformed;
441 return Result::Unsupported;
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;
452 return Result::Valid;
455ModuleImage::Result ModuleImage::validateRelocations() {
456 uintptr_t previous = 0;
457 for (
size_t i = 0; i < relocationCount; ++i) {
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;
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;
473 if (kind == Relative) {
474 if (R_SYM(value.info) || value.addend < 0 || !contains(value.addend, 1)) {
475 return Result::Malformed;
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;
485 (value.addend < 0 || !range(target.value, value.addend, MaximumMappedBytes) ||
486 !contains(target.value + value.addend, 1))) {
487 return Result::Malformed;
491 return Result::Valid;
494bool ModuleImage::namedObject(
const char* wanted,
size_t size, Symbol& result)
const {
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))
502 const char* ownerName =
nullptr;
503 return string(sectionStrings, sectionStringBytes, owner.name, ownerName, SymbolNameBytes) &&
504 !StringCompare(ownerName,
".modinfo") && contains(result.value, size);
507bool ModuleImage::dependenciesAt(
const char* wanted,
char names[][NameBytes],
size_t& count,
508 bool optional)
const {
510 if (!findSymbol(wanted, list))
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))
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))
521 return i + 1 == list.size /
sizeof(uintptr_t);
522 if (count == MaximumDependencies)
525 if (!fileOffset(target, 1, offset))
527 const char* dependency =
nullptr;
528 if (!mappedString(target, dependency, NameBytes) || !*dependency)
530 const size_t length = StringLength(dependency);
531 for (
size_t j = 0; j < count; ++j) {
532 if (!StringCompare(names[j], dependency))
535 MemoryCopy(names[count++], dependency, length + 1);
540bool ModuleImage::lifecycle(
const char* startName,
const char* endName, uintptr_t* targets,
541 size_t& count)
const {
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))
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)) {
554 for (uintptr_t at = first.value; at < last.value; at +=
sizeof(uintptr_t)) {
555 uintptr_t target = 0;
556 if (!localPointer(at, target))
560 if (target == ~uintptr_t{0})
562 if (!contains(target, 1, PF_R | PF_X))
564 targets[count++] = target;
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;
579 if (!fileOffset(unloadable.value, 1, offset) || bytes[offset] != 1 ||
580 !fileOffset(runtimeUnloadable.value, 1, offset) || bytes[offset] != 1) {
581 return Result::Unsupported;
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;
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 ==
'-' ||
597 return Result::Unsupported;
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;
606 return Result::Valid;
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);
619bool ModuleImage::exportedSymbol(
size_t index, Symbol& result)
const {
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);
628bool ModuleImage::validateMaterialized(uintptr_t base)
const {
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) {
635 if (!findSymbol(objects[i], value)) {
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))
644 MemoryCopy(&actual,
reinterpret_cast<void*
>(base + value.value + offset),
sizeof(actual));
645 if (actual != (expected ? base + expected : 0))
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))
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) {
660 if (!findSymbol(starts[i], first) || !findSymbol(ends[i], last))
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))
666 MemoryCopy(&actual,
reinterpret_cast<void*
>(base + at),
sizeof(actual));
667 if (actual != ((!expected || expected == ~uintptr_t{0}) ? expected : base + expected))
671 const char* flags[] = {
"g_bModuleUnloadable",
"g_bModuleRuntimeUnloadable"};
672 for (
const char* flag : flags) {
674 if (!findSymbol(flag, value) || *
reinterpret_cast<const uint8_t*
>(base + value.value) != 1)
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)