#include <mach-o/fixup-chains.h>
#include <mach-o/loader.h>
#include "kernel-fixups.h"
#ifndef LC_FILESET_ENTRY
#define LC_FILESET_ENTRY (0x35 | LC_REQ_DYLD)
struct fileset_entry_command {
uint32_t cmd;
uint32_t cmdsize;
uint64_t vmaddr;
uint64_t fileoff;
union lc_str entry_id;
uint32_t reserved;
};
#endif
typedef int (*ModInitLogFunc)(const char*, ...);
static const int LogModInits = 0;
struct TestRunnerFunctions;
typedef int (*InitializerFunc)(const TestRunnerFunctions*);
#if __x86_64__
__attribute__((section(("__HIB, __text"))))
#else
__attribute__((section(("__TEXT_EXEC, __text"))))
#endif
static int getSlide(const struct mach_header* mh, ModInitLogFunc logFunc,
uintptr_t* slide) {
uint64_t textVMAddr = 0;
if (LogFixups) {
logFunc("[LOG] kernel-slide: mh %p\n", mh);
}
if (LogFixups) {
logFunc("[LOG] kernel-slide: parsing load commands\n");
}
const struct load_command* startCmds = 0;
if ( mh->magic == MH_MAGIC_64 )
startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header_64));
else if ( mh->magic == MH_MAGIC )
startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header));
else {
const uint32_t* h = (uint32_t*)mh;
return 1; }
const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + mh->sizeofcmds);
const struct load_command* cmd = startCmds;
for (uint32_t i = 0; i < mh->ncmds; ++i) {
if (LogFixups) {
logFunc("[LOG] kernel-slide: parsing load command %d with cmd=0x%x\n", i, cmd->cmd);
}
const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize);
if ( cmd->cmdsize < 8 ) {
return 1;
}
if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
return 1;
}
if ( cmd->cmd == LC_SEGMENT_64 ) {
const struct segment_command_64* seg = (const struct segment_command_64*)cmd;
if ( areEqual(seg->segname, "__TEXT") ) {
textVMAddr = seg->vmaddr;
}
}
cmd = nextCmd;
}
*slide = (uintptr_t)mh - textVMAddr;
return 0;
}
#if __x86_64__
__attribute__((section(("__HIB, __text"))))
#else
__attribute__((section(("__TEXT_EXEC, __text"))))
#endif
static int runAllModInitFunctions(const struct mach_header* mh, ModInitLogFunc logFunc,
const TestRunnerFunctions* funcs) {
uintptr_t slide = 0;
if ( getSlide(mh, logFunc, &slide) != 0 ) {
return 1;
}
const struct load_command* startCmds = 0;
if ( mh->magic == MH_MAGIC_64 )
startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header_64));
else if ( mh->magic == MH_MAGIC )
startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header));
else {
const uint32_t* h = (uint32_t*)mh;
return 1; }
const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + mh->sizeofcmds);
const struct load_command* cmd = startCmds;
for (uint32_t i = 0; i < mh->ncmds; ++i) {
if (LogModInits) {
logFunc("[LOG] kernel-mod-inits: parsing load command %d with cmd=0x%x\n", i, cmd->cmd);
}
const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize);
if ( cmd->cmdsize < 8 ) {
return 1;
}
if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
return 1;
}
if ( cmd->cmd == LC_SEGMENT_64 ) {
const struct segment_command_64* seg = (const struct segment_command_64*)cmd;
const struct section_64* const sectionsStart = (struct section_64*)((char*)seg + sizeof(struct segment_command_64));
const struct section_64* const sectionsEnd = §ionsStart[seg->nsects];
for (const struct section_64* sect = sectionsStart; sect < sectionsEnd; ++sect) {
const uint8_t type = sect->flags & SECTION_TYPE;
if ( LogModInits ) {
logFunc("[LOG] kernel-mod-inits: section: %s %s\n", sect->segname, sect->sectname);
}
if ( type == S_MOD_INIT_FUNC_POINTERS ) {
InitializerFunc* inits = (InitializerFunc*)(sect->addr + slide);
const uintptr_t count = sect->size / sizeof(uintptr_t);
if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) {
logFunc("[LOG] kernel-mod-inits: __mod_init_funcs section has malformed address range\n");
return 1;
}
for (uintptr_t j = 0; j < count; ++j) {
InitializerFunc func = inits[j];
#if __has_feature(ptrauth_calls)
func = (InitializerFunc)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
#endif
if ( LogModInits ) {
logFunc("[LOG] kernel-mod-inits: running mod init %p\n", (const void*)func);
}
int initResult = func(funcs);
if ( initResult != 0 ) {
logFunc("[LOG] kernel-mod-inits: mod init %p, result = %d\n", (const void*)func, initResult);
return 1;
}
}
}
}
}
cmd = nextCmd;
}
return 0;
}
#if __x86_64__
__attribute__((section(("__HIB, __text"))))
#else
__attribute__((section(("__TEXT_EXEC, __text"))))
#endif
static int runAllModInitFunctionsForAppCache(const struct mach_header* appCacheMH, ModInitLogFunc logFunc,
const TestRunnerFunctions* funcs) {
uintptr_t slide = 0;
if ( getSlide(appCacheMH, logFunc, &slide) != 0 ) {
return 1;
}
if (LogFixups) {
logFunc("[LOG] mod-init: appCacheMH %p\n", appCacheMH);
}
if (LogFixups) {
logFunc("[LOG] mod-init: parsing load commands\n");
}
const struct load_command* startCmds = 0;
if ( appCacheMH->magic == MH_MAGIC_64 )
startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header_64));
else if ( appCacheMH->magic == MH_MAGIC )
startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header));
else {
const uint32_t* h = (uint32_t*)appCacheMH;
return 1; }
const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + appCacheMH->sizeofcmds);
const struct load_command* cmd = startCmds;
for (uint32_t i = 0; i < appCacheMH->ncmds; ++i) {
if (LogFixups) {
logFunc("[LOG] mod-init: parsing load command %d with cmd=0x%x\n", i, cmd->cmd);
}
const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize);
if ( cmd->cmdsize < 8 ) {
return 1;
}
if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
return 1;
}
if ( cmd->cmd == LC_FILESET_ENTRY ) {
const struct fileset_entry_command* app_cache_cmd = (const struct fileset_entry_command*)cmd;
const char* name = (char*)app_cache_cmd + app_cache_cmd->entry_id.offset;
const struct mach_header* mh = (const struct mach_header*)(app_cache_cmd->vmaddr + slide);
if ( LogModInits ) {
logFunc("[LOG] mod-init: Running mod inits for %p: %s\n", mh, name);
}
int result = runAllModInitFunctions(mh, logFunc, funcs);
if (result != 0) {
return 1;
}
}
cmd = nextCmd;
}
return 0;
}
#if __x86_64__
__attribute__((section(("__HIB, __text"))))
#else
__attribute__((section(("__TEXT_EXEC, __text"))))
#endif
static int slideKextsInsideKernelCollection(const struct mach_header* appCacheMH, const void* basePointers[4],
FixupsLogFunc logFunc, const TestRunnerFunctions* funcs) {
uintptr_t slideAmount = 0;
if ( getSlide(appCacheMH, logFunc, &slideAmount) != 0 ) {
return 1;
}
if (LogFixups) {
logFunc("[LOG] slide-pageable: appCacheMH %p\n", appCacheMH);
}
if (LogFixups) {
logFunc("[LOG] slide-pageable: parsing load commands\n");
}
const struct load_command* startCmds = 0;
if ( appCacheMH->magic == MH_MAGIC_64 )
startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header_64));
else if ( appCacheMH->magic == MH_MAGIC )
startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header));
else {
const uint32_t* h = (uint32_t*)appCacheMH;
return 1; }
const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + appCacheMH->sizeofcmds);
const struct load_command* cmd = startCmds;
for (uint32_t i = 0; i < appCacheMH->ncmds; ++i) {
if (LogFixups) {
logFunc("[LOG] slide-pageable: parsing load command %d with cmd=0x%x\n", i, cmd->cmd);
}
const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize);
if ( cmd->cmdsize < 8 ) {
return 1;
}
if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
return 1;
}
if ( cmd->cmd == LC_FILESET_ENTRY ) {
const struct fileset_entry_command* app_cache_cmd = (const struct fileset_entry_command*)cmd;
const char* name = (char*)app_cache_cmd + app_cache_cmd->entry_id.offset;
const struct mach_header* mh = (const struct mach_header*)(app_cache_cmd->vmaddr + slideAmount);
if ( LogModInits ) {
logFunc("[LOG] slide-pageable: Sliding %p: %s\n", mh, name);
}
int slideReturnCode = slide(mh, basePointers, logFunc);
if ( slideReturnCode != 0 ) {
FAIL("mh slide = %d\n", slideReturnCode);
return 1;
}
}
cmd = nextCmd;
}
return 0;
}