From 5fd83771641d15c418f747bd343ba6738d3875f7 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sun, 9 May 2021 14:20:58 -0400 Subject: Import macOS userland adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106 --- system_cmds/gcore.tproj/dyld.c | 314 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 system_cmds/gcore.tproj/dyld.c (limited to 'system_cmds/gcore.tproj/dyld.c') diff --git a/system_cmds/gcore.tproj/dyld.c b/system_cmds/gcore.tproj/dyld.c new file mode 100644 index 0000000..92aeac1 --- /dev/null +++ b/system_cmds/gcore.tproj/dyld.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2016 Apple Inc. All rights reserved. + */ + +#include "options.h" +#include "dyld.h" +#include "utils.h" +#include "corefile.h" +#include "vm.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * WARNING WARNING WARNING + * + * Do not trust any of the data from the target task. + * + * A broken program may have damaged it, or a malicious + * program may have deliberately constructed something to + * cause us harm. + */ + +static const char warn_dyld_info[] = "dyld information is incomplete or damaged"; + +dyld_process_info +get_task_dyld_info(const task_t task) +{ + kern_return_t kret; + dyld_process_info dpi = _dyld_process_info_create(task, 0, &kret); + if (NULL == dpi) { + err_mach(kret, NULL, "_dlyd_process_info_create"); + } else { + dyld_process_state_info stateInfo; + + _dyld_process_info_get_state(dpi, &stateInfo); + switch (stateInfo.dyldState) { + case dyld_process_state_not_started: + warnx("%s: dyld state %d", warn_dyld_info, stateInfo.dyldState); + _dyld_process_info_release(dpi); + dpi = NULL; + break; + default: + break; + } + } + return dpi; +} + +/* + * Get the shared cache UUID iff it's in use and is the system one + */ +bool +get_sc_uuid(dyld_process_info dpi, uuid_t uu) +{ + dyld_process_cache_info cacheInfo; + + _dyld_process_info_get_cache(dpi, &cacheInfo); + if (!cacheInfo.noCache && !cacheInfo.privateCache) { + uuid_copy(uu, cacheInfo.cacheUUID); + return true; + } + return false; +} + +void +free_task_dyld_info(dyld_process_info dpi) +{ + _dyld_process_info_release(dpi); +} + +/* + * This routine collects both the Mach-O header and the commands + * "below" it, assuming they're in contiguous memory. + */ +static native_mach_header_t * +copy_dyld_image_mh(task_t task, mach_vm_address_t targetmh, const char *path) +{ + vm_offset_t mhaddr = 0; + mach_msg_type_number_t mhlen = sizeof (native_mach_header_t); + + for (int attempts = 0; attempts < 2; attempts++) { + + const kern_return_t ret = mach_vm_read(task, targetmh, mhlen, &mhaddr, &mhlen); + if (KERN_SUCCESS != ret) { + err_mach(ret, NULL, "mach_vm_read() at 0x%llx for image %s\n", targetmh, path); + mhaddr = 0; + break; + } + const native_mach_header_t *mh = (void *)mhaddr; + if (mhlen < mh->sizeofcmds + sizeof (*mh)) { + const mach_msg_type_number_t newmhlen = sizeof (*mh) + mh->sizeofcmds; + mach_vm_deallocate(mach_task_self(), mhaddr, mhlen); + mhlen = newmhlen; + } else + break; + } + + native_mach_header_t *result = NULL; + + if (mhaddr) { + if (NULL != (result = malloc(mhlen))) + memcpy(result, (void *)mhaddr, mhlen); + mach_vm_deallocate(mach_task_self(), mhaddr, mhlen); + } + return result; +} + +/* + * This table (list) describes libraries and the executable in the address space + */ +struct liblist { + STAILQ_ENTRY(liblist) ll_linkage; + unsigned long ll_namehash; + struct libent ll_entry; +}; +static STAILQ_HEAD(, liblist) libhead = STAILQ_HEAD_INITIALIZER(libhead); + +static const struct libent * +libent_lookup_bypathname_withhash(const char *nm, const unsigned long hash) +{ + struct liblist *ll; + STAILQ_FOREACH(ll, &libhead, ll_linkage) { + if (hash != ll->ll_namehash) + continue; + struct libent *le = &ll->ll_entry; + if (strcmp(nm, le->le_pathname) == 0) + return le; + } + return NULL; +} + +const struct libent * +libent_lookup_byuuid(const uuid_t uuid) +{ + struct liblist *ll; + STAILQ_FOREACH(ll, &libhead, ll_linkage) { + struct libent *le = &ll->ll_entry; + if (uuid_compare(uuid, le->le_uuid) == 0) + return le; + } + return NULL; +} + +const struct libent * +libent_lookup_first_bytype(uint32_t mhtype) +{ + struct liblist *ll; + STAILQ_FOREACH(ll, &libhead, ll_linkage) { + struct libent *le = &ll->ll_entry; + if (mhtype == le->le_mh->filetype) + return le; + } + return NULL; +} + +const struct libent * +libent_insert(const char *rawnm, const uuid_t uuid, uint64_t mhaddr, const native_mach_header_t *mh, const struct vm_range *vr, mach_vm_offset_t objoff) +{ + const struct libent *le = libent_lookup_byuuid(uuid); + if (NULL != le) + return le; // disallow multiple names for the same uuid + + char *nm = realpath(rawnm, NULL); + if (NULL == nm) + nm = strdup(rawnm); + const unsigned long nmhash = simple_namehash(nm); + le = libent_lookup_bypathname_withhash(nm, nmhash); + if (NULL != le) { + free(nm); + return le; + } + + if (OPTIONS_DEBUG(opt, 3)) { + uuid_string_t uustr; + uuid_unparse_lower(uuid, uustr); + printf("[adding <'%s', %s, 0x%llx, %p", nm, uustr, mhaddr, mh); + if (vr) + printf(" (%llx-%llx)", V_ADDR(vr), V_ENDADDR(vr)); + printf(">]\n"); + } + struct liblist *ll = malloc(sizeof (*ll)); + ll->ll_namehash = nmhash; + ll->ll_entry.le_pathname = nm; + ll->ll_entry.le_filename = strrchr(ll->ll_entry.le_pathname, '/'); + if (NULL == ll->ll_entry.le_filename) + ll->ll_entry.le_filename = ll->ll_entry.le_pathname; + else + ll->ll_entry.le_filename++; + uuid_copy(ll->ll_entry.le_uuid, uuid); + ll->ll_entry.le_mhaddr = mhaddr; + ll->ll_entry.le_mh = mh; + if (vr) + ll->ll_entry.le_vr = *vr; + else { + V_SETADDR(&ll->ll_entry.le_vr, MACH_VM_MAX_ADDRESS); + V_SETSIZE(&ll->ll_entry.le_vr, 0); + } + ll->ll_entry.le_objoff = objoff; + STAILQ_INSERT_HEAD(&libhead, ll, ll_linkage); + + return &ll->ll_entry; +} + +bool +libent_build_nametable(task_t task, dyld_process_info dpi) +{ + __block bool valid = true; + + _dyld_process_info_for_each_image(dpi, ^(uint64_t mhaddr, const uuid_t uuid, const char *path) { + if (valid) { + native_mach_header_t *mh = copy_dyld_image_mh(task, mhaddr, path); + if (mh) { + /* + * Validate the rest of the mach information in the header before attempting optimizations + */ + const size_t mhlen = sizeof (*mh) + mh->sizeofcmds; + const struct load_command *lc = (const void *)(mh + 1); + struct vm_range vr = { + .addr = MACH_VM_MAX_ADDRESS, + .size = 0 + }; + mach_vm_offset_t objoff = MACH_VM_MAX_ADDRESS; + + for (unsigned n = 0; n < mh->ncmds; n++) { + if (((uintptr_t)lc & 0x3) != 0 || + (uintptr_t)lc < (uintptr_t)mh || (uintptr_t)lc > (uintptr_t)mh + mhlen) { + warnx("%s, %d", warn_dyld_info, __LINE__); + valid = false; + break; + } + switch (lc->cmd) { + case NATIVE_LC_SEGMENT: { + const native_segment_command_t *sc = (const void *)lc; + + char scsegname[17]; + strlcpy(scsegname, sc->segname, sizeof (scsegname)); + + if (0 == sc->vmaddr && + strcmp(scsegname, SEG_PAGEZERO) == 0) + break; + + /* + * -Depends- on finding a __TEXT segment first + * which implicitly maps the mach header too + */ + + if (MACH_VM_MAX_ADDRESS == objoff) { + if (strcmp(scsegname, SEG_TEXT) == 0) { + objoff = mhaddr - sc->vmaddr; + V_SETADDR(&vr, mhaddr); + V_SETSIZE(&vr, sc->vmsize); + } else { + printf("%s: expected %s segment, found %s\n", path, SEG_TEXT, scsegname); + valid = false; + break; + } + } + + mach_vm_offset_t lo = sc->vmaddr + objoff; + mach_vm_offset_t hi = lo + sc->vmsize; + + if (V_SIZE(&vr)) { + if (lo < V_ADDR(&vr)) { + mach_vm_offset_t newsize = V_SIZE(&vr) + (V_ADDR(&vr) - lo); + V_SETSIZE(&vr, newsize); + V_SETADDR(&vr, lo); + } + if (hi > V_ENDADDR(&vr)) { + V_SETSIZE(&vr, (hi - V_ADDR(&vr))); + } + } else { + V_SETADDR(&vr, lo); + V_SETSIZE(&vr, hi - lo); + } + assert(lo >= V_ADDR(&vr) && hi <= V_ENDADDR(&vr)); + } break; +#if defined(RDAR_28040018) + case LC_ID_DYLINKER: + if (MH_DYLINKER == mh->filetype) { + /* workaround: the API doesn't always return the right name */ + const struct dylinker_command *dc = (const void *)lc; + path = dc->name.offset + (const char *)dc; + } + break; +#endif + default: + break; + } + if (NULL == (lc = next_lc(lc))) + break; + } + if (valid) + (void) libent_insert(path, uuid, mhaddr, mh, &vr, objoff); + } + } + }); + if (OPTIONS_DEBUG(opt, 3)) + printf("nametable %sconstructed\n", valid ? "" : "NOT "); + return valid; +} -- cgit v1.2.3-56-ge451