From 1c51fdde6f257ffe2a1b92d1a783dab947211e95 Mon Sep 17 00:00:00 2001 From: Apple Date: Mon, 13 Aug 2001 18:14:33 +0000 Subject: [PATCH] system_cmds-196.tar.gz --- dynamic_pager.tproj/dynamic_pager.c | 16 +- fs_usage.tproj/fs_usage.1 | 8 +- fs_usage.tproj/fs_usage.c | 256 +++- init.tproj/init.c | 1 - kextd.tproj/KEXTD.c | 52 +- kextd.tproj/KEXTD.h | 6 +- kextd.tproj/KEXTD_main.c | 57 +- kextd.tproj/Makefile | 4 +- kextd.tproj/PB.project | 3 +- kextload.tproj/Makefile | 2 +- kextload.tproj/PB.project | 2 +- kextunload.tproj/Makefile | 2 +- kextunload.tproj/PB.project | 2 +- kmodload.tproj/Makefile | 6 +- kmodload.tproj/PB.project | 5 +- kmodload.tproj/kld_patch.c | 2124 +++++++++++++++++++++++++++ kmodload.tproj/kld_patch.h | 56 + kmodload.tproj/kmodload.c | 454 +++--- latency.tproj/latency.c | 215 ++- login.tproj/login.c | 1 + nvram.tproj/Makefile | 2 +- nvram.tproj/PB.project | 2 +- sc_usage.tproj/sc_usage.c | 128 +- sc_usage.tproj/trace.codes | 55 +- top.tproj/Makefile | 2 +- top.tproj/PB.project | 2 +- top.tproj/top.c | 131 +- 27 files changed, 2962 insertions(+), 632 deletions(-) create mode 100644 kmodload.tproj/kld_patch.c create mode 100644 kmodload.tproj/kld_patch.h diff --git a/dynamic_pager.tproj/dynamic_pager.c b/dynamic_pager.tproj/dynamic_pager.c index 51ce54c..30375b2 100644 --- a/dynamic_pager.tproj/dynamic_pager.c +++ b/dynamic_pager.tproj/dynamic_pager.c @@ -75,12 +75,22 @@ server_alert_loop( max_size + MAX_TRAILER_SIZE, TRUE)) != KERN_SUCCESS) return kr; + if ((kr = vm_protect(mach_task_self(), + (vm_address_t)bufRequest, + max_size + MAX_TRAILER_SIZE, + FALSE, VM_PROT_ALL)) != KERN_SUCCESS) + return kr; mlock(bufRequest, max_size + MAX_TRAILER_SIZE); if ((kr = vm_allocate(mach_task_self(), (vm_address_t *)&bufReply, max_size + MAX_TRAILER_SIZE, TRUE)) != KERN_SUCCESS) return kr; + if ((kr = vm_protect(mach_task_self(), + (vm_address_t)bufReply, + max_size + MAX_TRAILER_SIZE, + FALSE, VM_PROT_ALL)) != KERN_SUCCESS) + return kr; mlock(bufReply, max_size + MAX_TRAILER_SIZE); while(TRUE) { mr = mach_msg_overwrite_trap(&bufRequest->Head, MACH_RCV_MSG|options, @@ -274,7 +284,6 @@ paging_setup(flags, size, priority, low, high) off_t filesize = size; char subfile[512]; FILE *file_ptr; - kern_return_t error; file_count = 0; sprintf(subfile, "%s%d", fileroot, file_count); @@ -285,6 +294,8 @@ paging_setup(flags, size, priority, low, high) macx_swapon(subfile, flags, size, priority); if(hi_water) { + mach_msg_type_name_t poly; + daemon(0,0); if (mach_port_allocate(mach_task_self(), @@ -293,6 +304,9 @@ paging_setup(flags, size, priority, low, high) fprintf(stderr,"allocation of trigger port failed\n"); exit(1); } + /* create a send right on our local port */ + mach_port_extract_right(mach_task_self(), trigger_port, + MACH_MSG_TYPE_MAKE_SEND, &trigger_port, &poly); macx_triggers(high, low, HI_WAT_ALERT, trigger_port); if(low) { macx_triggers(high, diff --git a/fs_usage.tproj/fs_usage.1 b/fs_usage.tproj/fs_usage.1 index 2d3af74..67a1fba 100644 --- a/fs_usage.tproj/fs_usage.1 +++ b/fs_usage.tproj/fs_usage.1 @@ -14,9 +14,11 @@ in real-time. .Nm fs_usage presents an ongoing display of system call usage information pertaining to filesystem activity. By default -this includes all system processes. The information, however, -may be limited to include or exclude a specified list -of processes. +this includes all system processes except the running +fs_usage process, +Terminal, telnetd, sshd, rlogind, tcsh, csh and sh. +These defaluts can be overridden such that output is limited to +include or exclude a list of processes specified by the user. .Pp The output presented by .Nm fs_usage diff --git a/fs_usage.tproj/fs_usage.c b/fs_usage.tproj/fs_usage.c index 1027e59..465d0dc 100644 --- a/fs_usage.tproj/fs_usage.c +++ b/fs_usage.tproj/fs_usage.c @@ -60,6 +60,16 @@ cc -I. -DKERNEL_PRIVATE -O -o fs_usage fs_usage.c extern int errno; +/* + MAXCOLS controls when extra data kicks in. + MAX_WIDE_MODE_COLS controls -w mode to get even wider data in path. + If NUMPARMS changes to match the kernel, it will automatically + get reflected in the -w mode output. +*/ +#define NUMPARMS 23 +#define PATHLENGTH (NUMPARMS*sizeof(long)) +#define MAXCOLS 131 +#define MAX_WIDE_MODE_COLS (PATHLENGTH + 80) struct th_info { int in_filemgr; @@ -69,11 +79,11 @@ struct th_info { int arg2; int arg3; int arg4; - int vfslookup; int child_thread; int waited; double stime; - char pathname[32]; + long *pathptr; + char pathname[PATHLENGTH + 1]; /* add room for null terminator */ }; #define MAX_THREADS 512 @@ -234,6 +244,7 @@ int pids[MAX_PIDS]; int num_of_pids = 0; int exclude_pids = 0; +int exclude_default_pids = 1; struct kinfo_proc *kp_buffer = 0; int kp_nentries = 0; @@ -299,17 +310,22 @@ void leave() /* exit under normal conditions -- INT handler */ void sigwinch() { - initscr(); + if (!wideflag) + initscr(); } int exit_usage(myname) { fprintf(stderr, "Usage: %s [-e] [-w] [pid | cmd [pid | cmd]....]\n", myname); - fprintf(stderr, " -e exclude the pids specified from the sample\n"); + fprintf(stderr, " -e exclude the specified list of pids from the sample\n"); + fprintf(stderr, " and exclude fs_usage by default\n"); fprintf(stderr, " -w force wider, detailed, output\n"); fprintf(stderr, " pid selects process(s) to sample\n"); fprintf(stderr, " cmd selects process(s) matching command string to sample\n"); + fprintf(stderr, "\n%s will handle a maximum list of %d pids.\n\n", myname, MAX_PIDS); + fprintf(stderr, "By default (no options) the following processes are excluded from the output:\n"); + fprintf(stderr, "fs_usage, Terminal, telnetd, sshd, rlogind, tcsh, csh, sh\n\n"); exit(1); } @@ -350,9 +366,12 @@ main(argc, argv) switch(ch) { case 'e': exclude_pids = 1; + exclude_default_pids = 0; break; case 'w': wideflag = 1; + if (COLS < MAX_WIDE_MODE_COLS) + COLS = MAX_WIDE_MODE_COLS; break; default: exit_usage(myname); @@ -362,6 +381,10 @@ main(argc, argv) argc -= optind; argv += optind; + /* If we process any list of pids/cmds, then turn off the defaults */ + if (argc > 0) + exclude_default_pids = 0; + while (argc > 0 && num_of_pids < (MAX_PIDS - 1)) { select_pid_mode++; argtopid(argv[0]); @@ -369,8 +392,26 @@ main(argc, argv) argv++; } + /* Exclude a set of default pids */ + if (exclude_default_pids) + { + argtopid("Terminal"); + argtopid("telnetd"); + argtopid("sshd"); + argtopid("rlogind"); + argtopid("tcsh"); + argtopid("csh"); + argtopid("sh"); + exclude_pids = 1; + } + if (exclude_pids) + { + if (num_of_pids < (MAX_PIDS - 1)) pids[num_of_pids++] = getpid(); + else + exit_usage(myname); + } #if 0 for (i = 0; i < num_of_pids; i++) @@ -638,6 +679,7 @@ sample_sc() { kd_buf *kd; int i, count; + size_t needed; void read_command_map(); void create_map_entry(); @@ -665,7 +707,7 @@ sample_sc() for (i = 0; i < cur_max; i++) { th_state[i].thread = 0; - th_state[i].vfslookup = 0; + th_state[i].pathptr = (long *)0; th_state[i].pathname[0] = 0; } cur_max = 0; @@ -740,23 +782,31 @@ sample_sc() if ((ti = find_thread(thread, 0)) == (struct th_info *)0) continue; - if (ti->vfslookup == 0) { - ti->vfslookup = 1; - memset(&ti->pathname[0], 0, 32); + if (!ti->pathptr) { sargptr = (long *)&ti->pathname[0]; - + memset(&ti->pathname[0], 0, (PATHLENGTH + 1)); *sargptr++ = kd[i].arg2; *sargptr++ = kd[i].arg3; *sargptr++ = kd[i].arg4; + ti->pathptr = sargptr; + + } else { + sargptr = ti->pathptr; + + /* + We don't want to overrun our pathname buffer if the + kernel sends us more VFS_LOOKUP entries than we can + handle. + */ + + if ((long *)sargptr >= (long *)&ti->pathname[PATHLENGTH]) + continue; - } else if (ti->vfslookup == 1) { - ti->vfslookup = 2; - - sargptr = (long *)&ti->pathname[12]; *sargptr++ = kd[i].arg1; *sargptr++ = kd[i].arg2; *sargptr++ = kd[i].arg3; *sargptr++ = kd[i].arg4; + ti->pathptr = sargptr; } continue; } @@ -1401,8 +1451,13 @@ enter_syscall(int thread, int type, kd_buf *kd, char *name, double now) int usecs; long long l_usecs; long curr_time; - kd_threadmap *map; - kd_threadmap *find_thread_map(); + kd_threadmap *map; + kd_threadmap *find_thread_map(); + int clen = 0; + int tsclen = 0; + int nmclen = 0; + int argsclen = 0; + char buf[MAXCOLS]; switch (type) { @@ -1546,43 +1601,62 @@ enter_syscall(int thread, int type, kd_buf *kd, char *name, double now) bias_secs = curr_time - secs; } curr_time = bias_secs + secs; - printf("%-8.8s", &(ctime(&curr_time)[11])); + sprintf(buf, "%-8.8s", &(ctime(&curr_time)[11])); + tsclen = strlen(buf); - if (COLS > 110 || wideflag) { + if (COLS > MAXCOLS || wideflag) { usecs = l_usecs - (long long)((long long)secs * 1000000); - printf(".%03ld", (long)usecs / 1000); + sprintf(&buf[tsclen], ".%03ld", (long)usecs / 1000); + tsclen = strlen(buf); } - map = find_thread_map(thread); - if (map) { - char buf[128]; - int clen; - - sprintf(buf, " %s", name); - clen = strlen(buf); - - if (clen > 25) - clen = 25; - memset(&buf[clen], ' ', 26 - clen); - sprintf(&buf[25], "(%d, 0x%x, 0x%x, 0x%x)", (short)kd->arg1, kd->arg2, kd->arg3, kd->arg4); - clen = strlen(&buf[25]) + 25; - memset(&buf[clen], ' ', 128 - clen); - - if (COLS > 110 || wideflag) - sprintf(&buf[81], "%s\n", map->command); - else - sprintf(&buf[60], "%.12s\n", map->command); + /* Print timestamp column */ + printf(buf); + map = find_thread_map(thread); + if (map) { + sprintf(buf, " %-25.25s ", name); + nmclen = strlen(buf); printf(buf); + sprintf(buf, "(%d, 0x%x, 0x%x, 0x%x)", (short)kd->arg1, kd->arg2, kd->arg3, kd->arg4); + argsclen = strlen(buf); + + /* + Calculate white space out to command + */ + if (COLS > MAXCOLS || wideflag) + { + clen = COLS - (tsclen + nmclen + argsclen + 20); + } + else + clen = COLS - (tsclen + nmclen + argsclen + 12); + + if(clen > 0) + { + printf(buf); /* print the kdargs */ + memset(buf, ' ', clen); + buf[clen] = '\0'; + printf(buf); + } + else if ((argsclen + clen) > 0) + { + /* no room so wipe out the kdargs */ + memset(buf, ' ', (argsclen + clen)); + buf[argsclen + clen] = '\0'; + printf(buf); + } + + if (COLS > MAXCOLS || wideflag) + printf("%-20.20s\n", map->command); + else + printf("%-12.12s\n", map->command); } else printf(" %-24.24s (%5d, %#x, 0x%x, 0x%x)\n", name, (short)kd->arg1, kd->arg2, kd->arg3, kd->arg4); - } else { ti->in_filemgr = 0; } ti->thread = thread; - ti->vfslookup = 0; ti->waited = 0; ti->type = type; ti->stime = now; @@ -1590,6 +1664,7 @@ enter_syscall(int thread, int type, kd_buf *kd, char *name, double now) ti->arg2 = kd->arg2; ti->arg3 = kd->arg3; ti->arg4 = kd->arg4; + ti->pathptr = (long *)0; ti->pathname[0] = 0; break; @@ -1597,6 +1672,7 @@ enter_syscall(int thread, int type, kd_buf *kd, char *name, double now) default: break; } + fflush (0); } @@ -1612,6 +1688,9 @@ exit_syscall(char *sc_name, int thread, int type, int error, int retval, long curr_time; kd_threadmap *map; kd_threadmap *find_thread_map(); + int len = 0; + int clen = 0; + char buf[MAXCOLS]; if ((ti = find_thread(thread, type)) == (struct th_info *)0) return; @@ -1624,52 +1703,93 @@ exit_syscall(char *sc_name, int thread, int type, int error, int retval, bias_secs = curr_time - secs; } curr_time = bias_secs + secs; - printf("%-8.8s", &(ctime(&curr_time)[11])); + sprintf(buf, "%-8.8s", &(ctime(&curr_time)[11])); + clen = strlen(buf); - - if (COLS > 110 || wideflag) { + if (COLS > MAXCOLS || wideflag) { nopadding = 0; usecs = l_usecs - (long long)((long long)secs * 1000000); - printf(".%03ld", (long)usecs / 1000); - + sprintf(&buf[clen], ".%03ld", (long)usecs / 1000); + clen = strlen(buf); if ((type >> 24) != FILEMGR_CLASS) { if (find_thread(thread, -1)) { - printf(" "); + sprintf(&buf[clen], " "); + clen = strlen(buf); nopadding = 1; } } } else nopadding = 1; - if (((type >> 24) == FILEMGR_CLASS) && (COLS > 110 || wideflag)) - printf(" %-18.18s", sc_name); + if (((type >> 24) == FILEMGR_CLASS) && (COLS > MAXCOLS || wideflag)) + sprintf(&buf[clen], " %-18.18s", sc_name); else - printf(" %-15.15s", sc_name); + sprintf(&buf[clen], " %-15.15s", sc_name); - if (COLS > 110 || wideflag) { + clen = strlen(buf); + + if (COLS > MAXCOLS || wideflag) { if (has_fd == 2 && error == 0) - printf(" F=%-3d", retval); + sprintf(&buf[clen], " F=%-3d", retval); else if (has_fd == 1) - printf(" F=%-3d", ti->arg1); + sprintf(&buf[clen], " F=%-3d", ti->arg1); else if (has_ret != 2) - printf(" "); + sprintf(&buf[clen], " "); + + clen = strlen(buf); if (error) - printf("[%3d] ", error); + sprintf(&buf[clen], "[%3d] ", error); else if (has_ret == 3) - printf("O=0x%8.8x", ti->arg3); + sprintf(&buf[clen], "O=0x%8.8x", ti->arg3); else if (has_ret == 5) - printf("O=0x%8.8x", retval); + sprintf(&buf[clen], "O=0x%8.8x", retval); else if (has_ret == 2) - printf(" A=0x%8.8x ", retval); + sprintf(&buf[clen], " A=0x%8.8x ", retval); else if (has_ret == 1) - printf(" B=0x%-6x", retval); + sprintf(&buf[clen], " B=0x%-6x", retval); else if (has_ret == 4) - printf("R=0x%-8x", retval); + sprintf(&buf[clen], "R=0x%-8x", retval); else - printf(" "); + sprintf(&buf[clen], " "); + clen = strlen(buf); } - printf(" %-28.28s ", ti->pathname); + printf(buf); + + /* + Calculate space available to print pathname + */ + if (COLS > MAXCOLS || wideflag) + clen = COLS - (clen + 13 + 20); + else + clen = COLS - (clen + 13 + 12); + + if ((type >> 24) != FILEMGR_CLASS && !nopadding) + clen -= 3; + + sprintf(&buf[0], " %s ", ti->pathname); + len = strlen(buf); + + if (clen > len) + { + /* + Add null padding if column length + is wider than the pathname length. + */ + memset(&buf[len], ' ', clen - len); + buf[clen] = '\0'; + printf(buf); + } + else if (clen == len) + { + printf(buf); + } + else if ((clen > 0) && (clen < len)) + { + /* This prints the tail end of the pathname */ + buf[len-clen] = ' '; + printf(&buf[len - clen]); + } usecs = (unsigned long)(((double)now - ti->stime) / divisor); secs = usecs / 1000000; @@ -1685,16 +1805,17 @@ exit_syscall(char *sc_name, int thread, int type, int error, int retval, printf(" "); if (map) { - if (COLS > 110 || wideflag) - printf(" %s", map->command); + if (COLS > MAXCOLS || wideflag) + printf(" %-20.20s", map->command); else - printf(" %.12s", map->command); + printf(" %-12.12s", map->command); } printf("\n"); if (ti == &th_state[cur_max - 1]) cur_max--; ti->thread = 0; + fflush (0); } int @@ -1819,8 +1940,13 @@ void create_map_entry(int thread, char *command) map->valid = 1; map->thread = thread; - (void)strncpy (map->command, command, sizeof(map->command)); - map->command[sizeof(map->command)-1] = '\0'; + /* + The trace entry that returns the command name will hold + at most, MAXCOMLEN chars, and in that case, is not + guaranteed to be null terminated. + */ + (void)strncpy (map->command, command, MAXCOMLEN); + map->command[MAXCOMLEN] = '\0'; } diff --git a/init.tproj/init.c b/init.tproj/init.c index a44eb7a..1dd3015 100644 --- a/init.tproj/init.c +++ b/init.tproj/init.c @@ -542,7 +542,6 @@ setctty(name, flags) int fd; (void) revoke(name); - sleep (2); /* leave DTR low */ if ((fd = open(name, flags | O_RDWR)) == -1) { stall("can't open %s: %m", name); _exit(1); diff --git a/kextd.tproj/KEXTD.c b/kextd.tproj/KEXTD.c index b00a232..2030b48 100644 --- a/kextd.tproj/KEXTD.c +++ b/kextd.tproj/KEXTD.c @@ -838,7 +838,7 @@ static KEXTReturn _KEXTDModuleErrorCB(KEXTManagerRef manager, KEXTModuleRef modu #if TIMERSOURCE static void _KEXTDTimerCallout(CFRunLoopTimerRef timer, void * info) { - KEXTDScanPaths((KEXTDRef)info); + KEXTDScanPaths((KEXTDRef)info, false); } #endif @@ -853,7 +853,7 @@ static void _KEXTDSIGHUPCallout(void * info) // Check for new or removed bundles and do the appropriate // things. - KEXTDScanPaths((KEXTDRef)info); + KEXTDScanPaths((KEXTDRef)info, false); // Make sure we try to load the unloaded personalities // It's probably overkill to do this here. @@ -870,7 +870,7 @@ static void _KEXTDPerform(void * info) k = (KEXTD *)info; -// KEXTDScanPaths((KEXTDRef)k); +// KEXTDScanPaths((KEXTDRef)k, false); PTLockTakeLock(k->_queue_lock); while ( !queue_empty(&k->_requestQ) ) { @@ -886,13 +886,7 @@ static void _KEXTDPerform(void * info) name = reqstruct->kmodname; free(reqstruct); - if( type == kIOCatalogMatchIdle) { - - mach_timespec_t timeout = { 10, 0 }; - IOKitWaitQuiet( k->_catPort, &timeout ); - KEXTdaemonSignal(); - - } else if ( name ) { + if ( name ) { if ( k->_beVerbose ) { char modname[256]; @@ -1038,7 +1032,7 @@ void KEXTDReset(KEXTDRef kextd) if ( k->_manager ) KEXTManagerReset(k->_manager); - KEXTDScanPaths(kextd); + KEXTDScanPaths(kextd, false); } static KEXTReturn _KEXTDSendDataToCatalog(KEXTDRef kextd, int flag, CFTypeRef obj) @@ -1123,10 +1117,10 @@ static KEXTReturn _KEXTDSendPersonalities(KEXTDRef kextd, KEXTBootlevel bootleve if ( CFArrayGetCount(toload) > 0 ) { error = KEXTManagerLoadPersonalities(((KEXTD *)kextd)->_manager, toload); - } else { - KEXTdaemonSignal(); } + KEXTdaemonSignal(); + CFRelease(toload); return error; @@ -1355,7 +1349,6 @@ KEXTReturn KEXTDKernelRequest(KEXTDRef kextd, CFStringRef name) static void * _KEXTDKmodWait(void * info) { mach_port_t kmodPort; - kern_return_t kr; KEXTD * kextd; KEXTReturn error; request_t * reqstruct; @@ -1464,7 +1457,7 @@ KEXTdaemonSignal(void) return; signalled = TRUE; if (gDebug) { - printf("kextd: idle\n"); + printf("kextd: signal\n"); return; } @@ -1486,11 +1479,17 @@ static void KEXTdaemonWait(void) { kern_return_t kr; + mach_port_t masterPort; mach_timespec_t waitTime = { 40, 0 }; kr = semaphore_timedwait( gDaemonSema, waitTime ); if( kr != KERN_SUCCESS ) syslog(LOG_ERR, "semaphore_timedwait(%lx)\n", kr); + + IOMasterPort( MACH_PORT_NULL, &masterPort ); + IOKitWaitQuiet( masterPort, &waitTime ); + if( kr != KERN_SUCCESS ) + syslog(LOG_ERR, "IOKitWaitQuiet(%lx)\n", kr); } static int @@ -1547,9 +1546,9 @@ KEXTdaemon(nochdir, noclose) #if TIMERSOURCE -KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, Boolean debug, Boolean poll, CFIndex period, KEXTBootlevel bootlevel) +KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, Boolean debug, Boolean poll, CFIndex period, KEXTBootlevel bootlevel, Boolean cdMKextBoot) #else -KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, Boolean debug, KEXTBootlevel bootlevel) +KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, Boolean debug, KEXTBootlevel bootlevel, Boolean cdMKextBoot) #endif { pthread_attr_t kmod_thread_attr; @@ -1585,7 +1584,7 @@ KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, B }; gDebug = debug; - if (!debug) { + if (!debug && !cdMKextBoot) { errno = 0; KEXTdaemon(0, 0); if ( errno ) { @@ -1616,6 +1615,11 @@ KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, B _kextd = kextd; + if( cdMKextBoot) { + KEXTDScanPaths(kextd, true); + return kKEXTReturnSuccess; + } + // FIXME: Need a way to make this synchronous! error = KERN2KEXTReturn(IOCatalogueSendData(k->_catPort, kIOCatalogRemoveKernelLinker, 0, 0)); if (error != kKEXTReturnSuccess) { @@ -1672,7 +1676,7 @@ KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, B syslog(LOG_INFO, "started."); IOCatalogueReset(k->_catPort, kIOCatalogResetDefault); - KEXTDScanPaths(kextd); + KEXTDScanPaths(kextd, false); #if TIMERSOURCE if ( poll ) { @@ -1705,7 +1709,7 @@ KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, B return kKEXTReturnSuccess; } -void KEXTDScanPaths(KEXTDRef kextd) +void KEXTDScanPaths(KEXTDRef kextd, Boolean cdMKextBoot) { KEXTReturn error; KEXTD * k; @@ -1732,9 +1736,11 @@ void KEXTDScanPaths(KEXTDRef kextd) syslog(LOG_INFO, "scanning: %s.", str); } } - error = KEXTManagerScanPath(k->_manager, url); - if ( error != kKEXTReturnSuccess ) { - syslog(LOG_ERR, "error (%d) scanning path.\n", error); + if( !cdMKextBoot) { + error = KEXTManagerScanPath(k->_manager, url); + if ( error != kKEXTReturnSuccess ) { + syslog(LOG_ERR, "error (%d) scanning path.\n", error); + } } #if LOOKAPPLENDRV do { diff --git a/kextd.tproj/KEXTD.h b/kextd.tproj/KEXTD.h index cdd6088..9cd5055 100644 --- a/kextd.tproj/KEXTD.h +++ b/kextd.tproj/KEXTD.h @@ -47,11 +47,11 @@ void KEXTDFree(KEXTDRef kextd); void KEXTDHangup(KEXTDRef kextd); void KEXTDReset(KEXTDRef kextd); #if TIMERSOURCE -KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, Boolean debug, Boolean poll, CFIndex period, KEXTBootlevel bootlevel); +KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, Boolean debug, Boolean poll, CFIndex period, KEXTBootlevel bootlevel, Boolean cdMKextBoot); #else -KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, Boolean debug, KEXTBootlevel bootlevel); +KEXTReturn KEXTDStartMain(KEXTDRef kextd, Boolean beVerbose, Boolean safeBoot, Boolean debug, KEXTBootlevel bootlevel, Boolean cdMKextBoot); #endif -void KEXTDScanPaths(KEXTDRef kextd); +void KEXTDScanPaths(KEXTDRef kextd, Boolean cdMKextBoot); void KEXTDAddScanPath(KEXTDRef kextd, CFURLRef path); void KEXTDRegisterHelperCallbacks(KEXTDRef kextd, KEXTDHelperCallbacks * callbacks); KEXTReturn KEXTDKernelRequest(KEXTDRef kextd, CFStringRef moduleName); diff --git a/kextd.tproj/KEXTD_main.c b/kextd.tproj/KEXTD_main.c index 0537f47..778b187 100644 --- a/kextd.tproj/KEXTD_main.c +++ b/kextd.tproj/KEXTD_main.c @@ -9,7 +9,7 @@ static const char * arg0 = NULL; static void usage(void) { - printf("usage: %s [-v] [-d] [-x] [-b bootlevel]\n", arg0); + printf("usage: %s [-v] [-d] [-x] [-j] [-b bootlevel] [-f dirpath]\n", arg0); exit(1); } @@ -18,14 +18,15 @@ int main (int argc, const char *argv[]) KEXTDRef kextd; KEXTReturn error; KEXTBootlevel bootlevel; + CFStringRef str; CFURLRef url; - CFArrayRef array; + CFMutableArrayRef array; CFIndex period; Boolean safeBoot; Boolean beVerbose; Boolean enableTimer; Boolean debug; - const void * vals[1]; + Boolean cdMKextBoot; int c; arg0 = argv[0]; @@ -34,14 +35,27 @@ int main (int argc, const char *argv[]) safeBoot = false; beVerbose = false; enableTimer = false; + cdMKextBoot = false; bootlevel = kKEXTBootlevelNormal; - while ( (c = getopt(argc, (char **)argv, "xvdb:")) != -1 ) { + url = CFURLCreateWithFileSystemPath(NULL, CFSTR(DEFAULT_SEARCH_PATH), kCFURLPOSIXPathStyle, true); + if ( !url ) { + printf("Error opening: %s.\n", DEFAULT_SEARCH_PATH); + exit(1); + } + array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(array, url); + CFRelease(url); + + while ( (c = getopt(argc, (char **)argv, "xvdjb:f:")) != -1 ) { switch ( c ) { case 'x': safeBoot = true; break; + case 'v': + beVerbose = true; + break; case 'd': debug = true; break; @@ -68,6 +82,27 @@ int main (int argc, const char *argv[]) } break; + case 'j': + cdMKextBoot = true; + break; + + case 'f': + if ( !optarg ) { + usage(); + } + str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingNonLossyASCII); + if ( str ) + url = CFURLCreateWithFileSystemPath(NULL, str, kCFURLPOSIXPathStyle, true); + else + url = NULL; + if ( !url ) { + printf("Error opening: %s.\n", optarg); + } else { + CFArrayAppendValue(array, url); + CFRelease(url); + } + break; + default: usage(); } @@ -79,16 +114,6 @@ int main (int argc, const char *argv[]) usage(); } - - - url = CFURLCreateWithFileSystemPath(NULL, CFSTR(DEFAULT_SEARCH_PATH), kCFURLPOSIXPathStyle, true); - if ( !url ) { - printf("Error opening: %s.\n", DEFAULT_SEARCH_PATH); - exit(1); - } - vals[0] = url; - array = CFArrayCreate(NULL, vals, 1, &kCFTypeArrayCallBacks); - CFRelease(url); kextd = KEXTDCreate(array, &error); CFRelease(array); if ( !kextd ) { @@ -98,9 +123,9 @@ int main (int argc, const char *argv[]) KEXTDRegisterHelperCallbacks(kextd, NULL); #if TIMERSOURCE - error = KEXTDStartMain(kextd, beVerbose, safeBoot, debug, enableTimer, period, bootlevel); + error = KEXTDStartMain(kextd, beVerbose, safeBoot, debug, enableTimer, period, bootlevel, cdMKextBoot); #else - error = KEXTDStartMain(kextd, beVerbose, safeBoot, debug, bootlevel); + error = KEXTDStartMain(kextd, beVerbose, safeBoot, debug, bootlevel, cdMKextBoot); #endif if ( error != kKEXTReturnSuccess ) { KEXTDFree(kextd); diff --git a/kextd.tproj/Makefile b/kextd.tproj/Makefile index 56510b3..a43517e 100644 --- a/kextd.tproj/Makefile +++ b/kextd.tproj/Makefile @@ -31,8 +31,8 @@ PROF_LIBS = $(LIBS) NEXTSTEP_PB_CFLAGS = -fpascal-strings -Wno-four-char-constants -NEXTSTEP_PB_LDFLAGS = -undefined warning -FRAMEWORKS = -framework IOKit + +FRAMEWORKS = -framework CoreFoundation -framework IOKit NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc diff --git a/kextd.tproj/PB.project b/kextd.tproj/PB.project index 6a19916..dd503bf 100644 --- a/kextd.tproj/PB.project +++ b/kextd.tproj/PB.project @@ -1,7 +1,7 @@ { DYNAMIC_CODE_GEN = YES; FILESTABLE = { - FRAMEWORKS = (IOKit.framework); + FRAMEWORKS = (CoreFoundation.framework, IOKit.framework); FRAMEWORKSEARCH = (); H_FILES = (GetSymbolFromPEF.h, KEXTD.h, PTLock.h); OTHER_LINKED = (KEXTD.c, KEXTD_main.c, PEFSupport.c, PTLock.c); @@ -14,7 +14,6 @@ NEXTSTEP_COMPILEROPTIONS = "-fpascal-strings -Wno-four-char-constants"; NEXTSTEP_INSTALLDIR = /usr/libexec; NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; - NEXTSTEP_LINKEROPTIONS = "-undefined warning"; NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; PDO_UNIX_INSTALLDIR = /bin; diff --git a/kextload.tproj/Makefile b/kextload.tproj/Makefile index 0e72c07..d09afc4 100644 --- a/kextload.tproj/Makefile +++ b/kextload.tproj/Makefile @@ -28,7 +28,7 @@ DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) -FRAMEWORKS = -framework IOKit +FRAMEWORKS = -framework CoreFoundation -framework IOKit NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc diff --git a/kextload.tproj/PB.project b/kextload.tproj/PB.project index bf00197..d23d6b3 100644 --- a/kextload.tproj/PB.project +++ b/kextload.tproj/PB.project @@ -1,7 +1,7 @@ { DYNAMIC_CODE_GEN = YES; FILESTABLE = { - FRAMEWORKS = (IOKit.framework); + FRAMEWORKS = (CoreFoundation.framework, IOKit.framework); OTHER_LINKED = (kextload_main.c); OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble, kextload.8); SUBPROJECTS = (); diff --git a/kextunload.tproj/Makefile b/kextunload.tproj/Makefile index ab6a55b..2df3398 100644 --- a/kextunload.tproj/Makefile +++ b/kextunload.tproj/Makefile @@ -28,7 +28,7 @@ DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) -FRAMEWORKS = -framework IOKit +FRAMEWORKS = -framework CoreFoundation -framework IOKit NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc diff --git a/kextunload.tproj/PB.project b/kextunload.tproj/PB.project index 7089f1b..3b29511 100644 --- a/kextunload.tproj/PB.project +++ b/kextunload.tproj/PB.project @@ -1,7 +1,7 @@ { DYNAMIC_CODE_GEN = YES; FILESTABLE = { - FRAMEWORKS = (IOKit.framework); + FRAMEWORKS = (CoreFoundation.framework, IOKit.framework); OTHER_LINKED = (kextunload_main.c); OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble, kextunload.8); }; diff --git a/kmodload.tproj/Makefile b/kmodload.tproj/Makefile index 3704c8f..508f918 100644 --- a/kmodload.tproj/Makefile +++ b/kmodload.tproj/Makefile @@ -12,7 +12,9 @@ NAME = kmodload PROJECTVERSION = 2.8 PROJECT_TYPE = Tool -CFILES = kmodload.c +HFILES = kld_patch.h + +CFILES = kld_patch.c kmodload.c OTHERSRCS = Makefile.preamble Makefile Makefile.postamble kmodload.8\ kmodsyms.8 @@ -29,8 +31,6 @@ DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) -HEADER_PATHS =\ - -I$(NEXT_ROOT)/System/Library/Frameworks/System.framework/PrivateHeaders NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc diff --git a/kmodload.tproj/PB.project b/kmodload.tproj/PB.project index b8fba6b..474d5fe 100644 --- a/kmodload.tproj/PB.project +++ b/kmodload.tproj/PB.project @@ -2,9 +2,10 @@ DYNAMIC_CODE_GEN = YES; FILESTABLE = { FRAMEWORKS = (); - HEADERSEARCH = ("$(NEXT_ROOT)/System/Library/Frameworks/System.framework/PrivateHeaders"); + HEADERSEARCH = (); + H_FILES = (kld_patch.h); OTHER_LIBS = (kld); - OTHER_LINKED = (kmodload.c); + OTHER_LINKED = (kld_patch.c, kmodload.c); OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble, kmodload.8, kmodsyms.8); }; LANGUAGE = English; diff --git a/kmodload.tproj/kld_patch.c b/kmodload.tproj/kld_patch.c new file mode 100644 index 0000000..646bf14 --- /dev/null +++ b/kmodload.tproj/kld_patch.c @@ -0,0 +1,2124 @@ +/* + * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * History: + * 2001-05-30 gvdl Initial implementation of the vtable patcher. + */ +// 45678901234567890123456789012345678901234567890123456789012345678901234567890 + +#include +#include +#include +#include + +#if KERNEL + +#include +#include + +#include + +#include + +#include +#include + +#include "mach_loader.h" + +#include + +enum { false = 0, true = 1 }; + +#define vm_page_size page_size + +extern load_return_t fatfile_getarch( + void * vp, // normally a (struct vnode *) + vm_offset_t data_ptr, + struct fat_arch * archret); + +#else /* !KERNEL */ +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#endif /* KERNEL */ + +#include "kld_patch.h" + +#if 0 +static __inline__ void DIE(void) { IODelay(2000000000); } + +#define LOG_DELAY() IODelay(200000) +#define DEBUG_LOG(x) do { IOLog x; LOG_DELAY(); } while(0) +#else + +#define DIE() +#define LOG_DELAY() +#define DEBUG_LOG(x) + +#endif + +// OSObject symbol prefixes and suffixes +#define kVTablePrefix "___vt" +#define kReservedPrefix "__RESERVED" +#define kSuperClassSuffix ".superClass" +#define kGMetaSuffix ".gMetaClass" +#define kLinkEditSegName SEG_LINKEDIT + +// GCC 2.95 drops 2 leading constants in the vtable +#define kVTablePreambleLen 2 + +// Last address that I'm willing to try find vm in +#define kTopAddr ((unsigned char *) (1024 * 1024 * 1024)) + +// Size in bytes that Data Ref object's get increased in size +// Must be a power of 2 +#define kDataCapacityIncrement 128 + +// My usual set of helper macros. I personally find these macros +// easier to read in the code rather than an explicit error condition +// check. If I don't make it easy then I may get lazy ond not check +// everything. I'm sorry if you find this code harder to read. + +// break_if will evaluate the expression and if it is true +// then it will print the msg, which is enclosed in parens +// and then break. Usually used in loops are do { } while (0) +#define break_if(expr, msg) \ + if (expr) { \ + errprintf msg; \ + break; \ + } + +// return_if will evaluate expr and if true it will log the +// msg, which is enclosed in parens, and then it will return +// with the return code of ret. +#define return_if(expr, ret, msg) do { \ + if (expr) { \ + errprintf msg; \ + return ret; \ + } \ +} while (0) + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif /* MIN */ +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif /* MAX */ + +typedef struct Data { + unsigned long fLength, fCapacity; + unsigned char *fData; +} Data, *DataRef; + +struct sectionRecord { + const struct section *fSection; + DataRef fRelocCache; +}; + +enum patchState { + kSymbolIdentical, + kSymbolLocal, + kSymbolPadUpdate, + kSymbolSuperUpdate, + kSymbolMismatch +}; + +struct patchRecord { + struct nlist *fSymbol; + enum patchState fType; +}; + +struct relocRecord { + void *fValue; + const struct nlist *fSymbol; + struct relocation_info *fRInfo; + void *reserved; +}; + +struct metaClassRecord { + char *fSuperName; + struct fileRecord *fFile; + const struct nlist *fVTableSym; + struct patchRecord *fPatchedVTable; + char fClassName[1]; +}; + +struct fileRecord { + size_t fMapSize, fMachOSize; + const char *fPath; + unsigned char *fMap, *fMachO, *fPadEnd; + DataRef fClassList; + DataRef fSectData; + DataRef fNewSymbols, fNewStrings; + struct symtab_command *fSymtab; + struct sectionRecord *fSections; + char *fStringBase; + struct nlist *fSymbolBase; + const struct nlist *fLocalSyms; + unsigned int fNSects; + int fNLocal; + int fNewStringsLen; + Boolean fIsKernel, fNoKernelExecutable, fIsKmem; + Boolean fImageDirty, fSymbolsDirty; +}; + +static DataRef sFilesTable; +static struct fileRecord *sKernelFile; + +static DataRef sMergedFiles; +static DataRef sMergeMetaClasses; +static Boolean sMergedKernel; + +static void errprintf(const char *fmt, ...) +{ + extern void kld_error_vprintf(const char *format, va_list ap); + + va_list ap; + + va_start(ap, fmt); + kld_error_vprintf(fmt, ap); + va_end(ap); + +DIE(); +} + +static __inline__ unsigned long DataGetLength(DataRef data) +{ + return data->fLength; +} + +static __inline__ unsigned char *DataGetPtr(DataRef data) +{ + return data->fData; +} + + +static __inline__ Boolean DataContainsAddr(DataRef data, void *vAddr) +{ + unsigned char *addr = vAddr; + + return (data->fData <= addr) && (addr < data->fData + data->fLength); +} + +static __inline__ Boolean DataAddLength(DataRef data, unsigned long length) +{ + static Boolean DataSetLength(DataRef data, unsigned long length); + return DataSetLength(data, data->fLength + length); +} + +static __inline__ Boolean +DataAppendBytes(DataRef data, const void *addr, unsigned int len) +{ + unsigned long size = DataGetLength(data); + + if (!DataAddLength(data, len)) + return false; + + bcopy(addr, DataGetPtr(data) + size, len); + return true; +} + +static __inline__ Boolean DataAppendData(DataRef dst, DataRef src) +{ + return DataAppendBytes(dst, DataGetPtr(src), DataGetLength(src)); +} + +static Boolean DataSetLength(DataRef data, unsigned long length) +{ + // Don't bother to ever shrink a data object. + if (length > data->fCapacity) { + unsigned char *newData; + unsigned long newCapacity; + + newCapacity = length + kDataCapacityIncrement - 1; + newCapacity &= ~(kDataCapacityIncrement - 1); + newData = (unsigned char *) realloc(data->fData, newCapacity); + if (!newData) + return false; + + bzero(newData + data->fCapacity, newCapacity - data->fCapacity); + data->fData = newData; + data->fCapacity = newCapacity; + } + + data->fLength = length; + return true; +} + +static DataRef DataCreate(unsigned long length) +{ + DataRef data = (DataRef) malloc(sizeof(Data)); + + if (data) { + if (!length) + data->fCapacity = kDataCapacityIncrement; + else { + data->fCapacity = length + kDataCapacityIncrement - 1; + data->fCapacity &= ~(kDataCapacityIncrement - 1); + } + + data->fData = (unsigned char *) malloc(data->fCapacity); + if (!data->fData) { + free(data); + return NULL; + } + + bzero(data->fData, data->fCapacity); + data->fLength = length; + } + return data; +} + +static void DataRelease(DataRef data) +{ + if (data) { + if (data->fData) + free(data->fData); + data->fData = 0; + free(data); + } +} + +static const char * +symbolname(const struct fileRecord *file, const struct nlist *sym) +{ + unsigned long strsize; + long strx = sym->n_un.n_strx; + + if (strx >= 0) + return file->fStringBase + strx; + + strsize = file->fSymtab->strsize; + strx = -strx; + if (strx < strsize) + return file->fStringBase + strx; + + strx -= strsize; + return (char *) DataGetPtr(file->fNewStrings) + strx; +} + +static struct fileRecord *getFile(const char *path) +{ + if (sFilesTable) { + int i, nfiles; + struct fileRecord **files; + + // Check to see if we have already merged this file + nfiles = DataGetLength(sFilesTable) / sizeof(struct fileRecord *); + files = (struct fileRecord **) DataGetPtr(sFilesTable); + for (i = 0; i < nfiles; i++) { + if (!strcmp(path, files[i]->fPath)) + return files[i]; + } + } + + return NULL; +} + +static struct fileRecord * addFile(struct fileRecord *file) +{ + struct fileRecord *newFile; + + if (!sFilesTable) { + sFilesTable = DataCreate(0); + if (!sFilesTable) + return NULL; + } + + newFile = (struct fileRecord *) malloc(sizeof(struct fileRecord)); + if (!newFile) + return NULL; + + if (!DataAppendBytes(sFilesTable, &newFile, sizeof(newFile))) { + free(newFile); + return NULL; + } + + bcopy(file, newFile, sizeof(struct fileRecord)); + return newFile; +} + +// @@@ gvdl: need to clean up the sMergeMetaClasses +// @@@ gvdl: I had better fix the object file up again +static void removeFile(struct fileRecord *file) +{ + if (file->fClassList) { + DataRelease(file->fClassList); + file->fClassList = 0; + } + + if (file->fSectData) { + struct sectionRecord *section; + unsigned int i, nsect; + + nsect = file->fNSects; + section = file->fSections; + for (i = 0; i < nsect; i++, section++) { + if (section->fRelocCache) { + DataRelease(section->fRelocCache); + section->fRelocCache = 0; + } + } + + DataRelease(file->fSectData); + file->fSectData = 0; + file->fSections = 0; + file->fNSects = 0; + } + + if (file->fMap) { +#if KERNEL + if (file->fIsKmem) + kmem_free(kernel_map, (vm_address_t) file->fMap, file->fMapSize); +#else /* !KERNEL */ + if (file->fPadEnd) { + vm_address_t padVM; + vm_size_t padSize; + + padVM = round_page((vm_address_t) file->fMap + file->fMapSize); + padSize = (vm_size_t) ((vm_address_t) file->fPadEnd - padVM); + (void) vm_deallocate(mach_task_self(), padVM, padSize); + file->fPadEnd = 0; + } + + (void) munmap((caddr_t) file->fMap, file->fMapSize); +#endif /* !KERNEL */ + file->fMap = 0; + } + + file->fPath = 0; +} + +#if !KERNEL +static Boolean +mapObjectFile(struct fileRecord *file) +{ + Boolean result = false; + static unsigned char *sFileMapBaseAddr; + + int fd = 0; + + if (!sFileMapBaseAddr) { + kern_return_t ret; + vm_address_t probeAddr; + + // If we don't already have a base addr find any random chunk + // of 32 meg of VM and to use the 16 meg boundrary as a base. + ret = vm_allocate(mach_task_self(), &probeAddr, + 32 * 1024 * 1024, VM_FLAGS_ANYWHERE); + return_if(KERN_SUCCESS != ret, false, + ("Unable to allocate base memory %s\n", mach_error_string(ret))); + (void) vm_deallocate(mach_task_self(), probeAddr, 32 * 1024 * 1024); + + // Now round to the next 16 Meg boundrary + probeAddr = (probeAddr + (16 * 1024 * 1024 - 1)) + & ~(16 * 1024 * 1024 - 1); + sFileMapBaseAddr = (unsigned char *) probeAddr; + } + + fd = open(file->fPath, O_RDONLY, 0); + return_if(fd == -1, false, ("Can't open %s for reading - %s\n", + file->fPath, strerror(errno))); + + do { + kern_return_t ret; + struct stat sb; + int retaddr = -1; + + break_if(fstat(fd, &sb) == -1, + ("Can't stat %s - %s\n", file->fPath, strerror(errno))); + + file->fMapSize = sb.st_size; + file->fMap = sFileMapBaseAddr; + ret = KERN_SUCCESS; + while (file->fMap < kTopAddr) { + vm_address_t padVM; + vm_address_t padVMEnd; + vm_size_t padSize; + + padVM = round_page((vm_address_t) file->fMap + file->fMapSize); + retaddr = (int) mmap(file->fMap, file->fMapSize, + PROT_READ|PROT_WRITE, + MAP_FIXED|MAP_FILE|MAP_PRIVATE, + fd, 0); + if (-1 == retaddr) { + break_if(ENOMEM != errno, + ("mmap failed %d - %s\n", errno, strerror(errno))); + + file->fMap = (unsigned char *) padVM; + continue; + } + + + // Round up padVM to the next page after the file and assign at + // least another fMapSize more room rounded up to the next page + // boundary. + padVMEnd = round_page(padVM + file->fMapSize); + padSize = padVMEnd - padVM; + ret = vm_allocate( + mach_task_self(), &padVM, padSize, VM_FLAGS_FIXED); + if (KERN_SUCCESS == ret) { + file->fPadEnd = (unsigned char *) padVMEnd; + break; + } + else { + munmap(file->fMap, file->fMapSize); + break_if(KERN_INVALID_ADDRESS != ret, + ("Unable to allocate pad vm for %s - %s\n", + file->fPath, mach_error_string(ret))); + + file->fMap = (unsigned char *) padVMEnd; + continue; // try again wherever the vm system wants + } + } + + if (-1 == retaddr || KERN_SUCCESS != ret) + break; + + break_if(file->fMap >= kTopAddr, + ("Unable to map memory %s\n", file->fPath)); + + sFileMapBaseAddr = file->fPadEnd; + result = true; + } while(0); + + close(fd); + return result; +} +#endif /* !KERNEL */ + +static Boolean findBestArch(struct fileRecord *file) +{ + unsigned long magic; + struct fat_header *fat; + + + file->fMachOSize = file->fMapSize; + file->fMachO = file->fMap; + magic = ((const struct mach_header *) file->fMachO)->magic; + fat = (struct fat_header *) file->fMachO; + + // Try to figure out what type of file this is + return_if(file->fMapSize < sizeof(unsigned long), false, + ("%s isn't a valid object file - no magic\n", file->fPath)); + +#if KERNEL + + // CIGAM is byte-swapped MAGIC + if (magic == FAT_MAGIC || magic == FAT_CIGAM) { + + load_return_t load_return; + struct fat_arch fatinfo; + + load_return = fatfile_getarch(NULL, (vm_address_t) fat, &fatinfo); + return_if(load_return != LOAD_SUCCESS, false, + ("Extension \"%s\": has no code for this computer\n", file->fPath)); + + file->fMachO = file->fMap + fatinfo.offset; + file->fMachOSize = fatinfo.size; + magic = ((const struct mach_header *) file->fMachO)->magic; + } + +#else /* !KERNEL */ + + // Do we need to in-place swap the endianness of the fat header? + if (magic == FAT_CIGAM) { + unsigned long i; + struct fat_arch *arch; + + fat->nfat_arch = NXSwapBigLongToHost(fat->nfat_arch); + return_if(file->fMapSize < sizeof(struct fat_header) + + fat->nfat_arch * sizeof(struct fat_arch), + false, ("%s is too fat\n", file->fPath)); + + arch = (struct fat_arch *) &fat[1]; + for (i = 0; i < fat->nfat_arch; i++) { + arch[i].cputype = NXSwapBigLongToHost(arch[i].cputype); + arch[i].cpusubtype = NXSwapBigLongToHost(arch[i].cpusubtype); + arch[i].offset = NXSwapBigLongToHost(arch[i].offset); + arch[i].size = NXSwapBigLongToHost(arch[i].size); + arch[i].align = NXSwapBigLongToHost(arch[i].align); + } + + magic = NXSwapBigLongToHost(fat->magic); + } + + // Now see if we can find any valid architectures + if (magic == FAT_MAGIC) { + const NXArchInfo *myArch; + unsigned long fatsize; + struct fat_arch *arch; + + fatsize = sizeof(struct fat_header) + + fat->nfat_arch * sizeof(struct fat_arch); + return_if(file->fMapSize < fatsize, + false, ("%s isn't a valid fat file\n", file->fPath)); + + myArch = NXGetLocalArchInfo(); + arch = NXFindBestFatArch(myArch->cputype, myArch->cpusubtype, + (struct fat_arch *) &fat[1], fat->nfat_arch); + return_if(!arch, + false, ("%s hasn't got arch for %s\n", file->fPath, myArch->name)); + return_if(arch->offset + arch->size > file->fMapSize, + false, ("%s's %s arch is incomplete\n", file->fPath, myArch->name)); + file->fMachO = file->fMap + arch->offset; + file->fMachOSize = arch->size; + magic = ((const struct mach_header *) file->fMachO)->magic; + } + +#endif /* KERNEL */ + + return_if(magic != MH_MAGIC, + false, ("%s isn't a valid mach-o\n", file->fPath)); + + return true; +} + +static Boolean +parseSegments(struct fileRecord *file, struct segment_command *seg) +{ + struct sectionRecord *sections; + int i, nsects = seg->nsects; + const struct segmentMap { + struct segment_command seg; + const struct section sect[1]; + } *segMap; + + if (!nsects) { +#if KERNEL + // We don't need to look for the LinkEdit segment unless + // we are running in the kernel environment. + if (!strcmp(kLinkEditSegName, seg->segname)) { + // Grab symbol table from linkedit we will need this later + file->fSymbolBase = (void *) seg; + } +#endif + + return true; // Nothing more to do, so that was easy. + } + + if (!file->fSectData) { + file->fSectData = DataCreate(0); + if (!file->fSectData) + return false; + } + + // Increase length of section DataRef and cache data pointer + if (!DataAddLength(file->fSectData, nsects * sizeof(struct sectionRecord))) + return false; + file->fSections = (struct sectionRecord *) DataGetPtr(file->fSectData); + + // Initialise the new sections + sections = &file->fSections[file->fNSects]; + file->fNSects += nsects; + for (i = 0, segMap = (struct segmentMap *) seg; i < nsects; i++) + sections[i].fSection = &segMap->sect[i]; + + return true; +} + +// @@@ gvdl: These functions need to be hashed they are +// going to be way too slow for production code. +static const struct nlist * +findSymbolByAddress(const struct fileRecord *file, void *entry) +{ + // not quite so dumb linear search of all symbols + const struct nlist *sym; + int i, nsyms; + + // First try to find the symbol in the most likely place which is the + // extern symbols + sym = file->fLocalSyms; + for (i = 0, nsyms = file->fNLocal; i < nsyms; i++, sym++) { + if (sym->n_value == (unsigned long) entry && !(sym->n_type & N_STAB) ) + return sym; + } + + // Didn't find it in the external symbols so try to local symbols before + // giving up. + sym = file->fSymbolBase; + for (i = 0, nsyms = file->fSymtab->nsyms; i < nsyms; i++, sym++) { + if ( (sym->n_type & N_EXT) ) + return NULL; + if ( sym->n_value == (unsigned long) entry && !(sym->n_type & N_STAB) ) + return sym; + } + + return NULL; +} + +struct searchContext { + const char *fSymname; + const char *fStrbase; +}; + +static int symbolSearch(const void *vKey, const void *vSym) +{ + const struct searchContext *key = (const struct searchContext *) vKey; + const struct nlist *sym = (const struct nlist *) vSym; + + return strcmp(key->fSymname, key->fStrbase + sym->n_un.n_strx); +} + +static const struct nlist * +findSymbolByName(struct fileRecord *file, const char *symname) +{ + struct searchContext context; + + context.fSymname = symname; + context.fStrbase = file->fStringBase; + return (struct nlist *) + bsearch(&context, + file->fLocalSyms, file->fNLocal, sizeof(struct nlist), + symbolSearch); +} + +static Boolean +relocateSection(const struct fileRecord *file, struct sectionRecord *sectionRec) +{ + const struct nlist *symbol; + const struct section *section; + struct relocRecord *rec; + struct relocation_info *rinfo; + unsigned long i; + unsigned long r_address, r_symbolnum, r_length; + enum reloc_type_generic r_type; + UInt8 *sectionBase; + void **entry; + + sectionRec->fRelocCache = DataCreate( + sectionRec->fSection->nreloc * sizeof(struct relocRecord)); + if (!sectionRec->fRelocCache) + return false; + + section = sectionRec->fSection; + sectionBase = file->fMachO + section->offset; + + rec = (struct relocRecord *) DataGetPtr(sectionRec->fRelocCache); + rinfo = (struct relocation_info *) (file->fMachO + section->reloff); + for (i = 0; i < section->nreloc; i++, rec++, rinfo++) { + + // Totally uninterested in scattered relocation entries + if ( (rinfo->r_address & R_SCATTERED) ) + continue; + + r_address = rinfo->r_address; + entry = (void **) (sectionBase + r_address); + + /* + * The r_address field is really an offset into the contents of the + * section and must reference something inside the section (Note + * that this is not the case for PPC_RELOC_PAIR entries but this + * can't be one with the above checks). + */ + return_if(r_address >= section->size, false, + ("Invalid relocation entry in %s - not in section\n", file->fPath)); + + // If we don't have a VANILLA entry or the Vanilla entry isn't + // a 'long' then ignore the entry and try the next. + r_type = (enum reloc_type_generic) rinfo->r_type; + r_length = rinfo->r_length; + if (r_type != GENERIC_RELOC_VANILLA || r_length != 2) + continue; + + r_symbolnum = rinfo->r_symbolnum; + + /* + * If rinfo->r_extern is set this relocation entry is an external entry + * else it is a local entry. + */ + if (rinfo->r_extern) { + /* + * This is an external relocation entry. + * r_symbolnum is an index into the input file's symbol table + * of the symbol being refered to. The symbol must be + * undefined to be used in an external relocation entry. + */ + return_if(r_symbolnum >= file->fSymtab->nsyms, false, + ("Invalid relocation entry in %s - no symbol\n", file->fPath)); + + /* + * If this is an indirect symbol resolve indirection (all chains + * of indirect symbols have been resolved so that they point at + * a symbol that is not an indirect symbol). + */ + symbol = file->fSymbolBase; + if ((symbol[r_symbolnum].n_type & N_TYPE) == N_INDR) + r_symbolnum = symbol[r_symbolnum].n_value; + symbol = &symbol[r_symbolnum]; + + return_if(symbol->n_type != (N_EXT | N_UNDF), false, + ("Invalid relocation entry in %s - extern\n", file->fPath)); + } + else { + /* + * If the symbol is not in any section then it can't be a + * pointer to a local segment and I don't care about it. + */ + if (r_symbolnum == R_ABS) + continue; + + // Note segment references are offset by 1 from 0. + return_if(r_symbolnum > file->fNSects, false, + ("Invalid relocation entry in %s - local\n", file->fPath)); + + // Find the symbol, if any, that backs this entry + symbol = findSymbolByAddress(file, *entry); + } + + rec->fValue = *entry; // Save the previous value + rec->fRInfo = rinfo; // Save a pointer to the reloc + rec->fSymbol = symbol; // Record the current symbol + + *entry = (void *) rec; // Save pointer to record in object image + } + + ((struct fileRecord *) file)->fImageDirty = true; + + return true; +} + +static const struct nlist * +findSymbolRefAtLocation(const struct fileRecord *file, + struct sectionRecord *sctn, void **loc) +{ + if (file->fIsKernel) { + if (*loc) + return findSymbolByAddress(file, *loc); + } + else if (sctn->fRelocCache || relocateSection(file, sctn)) { + struct relocRecord *reloc = (struct relocRecord *) *loc; + + if (DataContainsAddr(sctn->fRelocCache, reloc)) + return reloc->fSymbol; + } + + return NULL; +} + +static Boolean +addClass(struct fileRecord *file, + struct metaClassRecord *inClass, + const char *cname) +{ + struct metaClassRecord *newClass = NULL; + struct metaClassRecord **fileClasses = NULL; + int len; + +if (!file->fIsKernel) { // @@@ gvdl: + DEBUG_LOG(("Adding Class %s\n", cname)); +} + + if (!file->fClassList) { + file->fClassList = DataCreate(0); + if (!file->fClassList) + return false; + } + + do { + // Attempt to allocate all necessary resource first + len = strlen(cname) + 1 + + (int) (&((struct metaClassRecord *) 0)->fClassName); + newClass = (struct metaClassRecord *) malloc(len); + if (!newClass) + break; + + if (!DataAddLength(file->fClassList, sizeof(struct metaClassRecord *))) + break; + fileClasses = (struct metaClassRecord **) + (DataGetPtr(file->fClassList) + DataGetLength(file->fClassList)); + + // Copy the meta Class structure and string name into newClass + // and insert object at end of the file->fClassList and sMergeMetaClasses + *newClass = *inClass; + strcpy(newClass->fClassName, cname); + fileClasses[-1] = newClass; + + return true; + } while (0); + + if (fileClasses) + DataAddLength(file->fClassList, -sizeof(struct metaClassRecord *)); + + if (newClass) + free(newClass); + + return false; +} + +static struct metaClassRecord *getClass(DataRef classList, const char *cname) +{ + if (classList) { + int i, nclass; + struct metaClassRecord **classes, *thisClass; + + nclass = DataGetLength(classList) / sizeof(struct metaClassRecord *); + classes = (struct metaClassRecord **) DataGetPtr(classList); + for (i = 0; i < nclass; i++) { + thisClass = classes[i]; + if (!strcmp(thisClass->fClassName, cname)) + return thisClass; + } + } + + return NULL; +} + +// Add the class 'cname' to the list of known OSObject based classes +// Note 'sym' is the .superClass symbol. +static Boolean +recordClass(struct fileRecord *file, const char *cname, const struct nlist *sym) +{ + char *supername = NULL; + const char *classname = NULL; + struct metaClassRecord newClass; + char strbuffer[1024]; + + // Only do the work actual work to find the super class if we are + // not currently working on the kernel. The kernel is the end + // of all superclass chains as by definition the kernel is binary + // compatible with itself. + if (!file->fIsKernel) { + const char *dot; + const struct nlist *supersym; + const struct section *section; + struct sectionRecord *sectionRec; + unsigned char sectind = sym->n_sect; + const char *superstr; + void **location; + + // We can't resolve anything that isn't in a real section + // Note that the sectind is starts at one to make room for the + // NO_SECT flag but the fNSects field isn't offset so we have a + // '>' test. Which means this isn't an OSObject based class + if (sectind == NO_SECT || sectind > file->fNSects) + return true; + + sectionRec = file->fSections + sectind - 1; + section = sectionRec->fSection; + location = (void **) ( file->fMachO + section->offset + + sym->n_value - section->addr ); + + supersym = findSymbolRefAtLocation(file, sectionRec, location); + if (!supersym) + return true; // No superclass symbol then it isn't an OSObject. + + // Find string in file and skip leading '_' and find last '.' + superstr = symbolname(file, supersym) + 1; + dot = strrchr(superstr, '.'); + if (!dot || strcmp(dot, kGMetaSuffix)) + return true; // Not an OSObject superclass so ignore it. + + supername = (char *) malloc(dot - superstr + 1); + strncpy(supername, superstr, dot - superstr); + supername[dot - superstr] = '\0'; + } + + do { + break_if(getClass(file->fClassList, cname), + ("Duplicate class %s in %s\n", cname, file->fPath)); + + snprintf(strbuffer, sizeof(strbuffer), "%s%s", kVTablePrefix, cname); + newClass.fVTableSym = findSymbolByName(file, strbuffer); + break_if(!newClass.fVTableSym, + ("Can't find vtable %s in %s\n", cname, file->fPath)); + + newClass.fFile = file; + newClass.fSuperName = supername; + newClass.fPatchedVTable = NULL; + + // Can't use cname as it may be a stack variable + // However the vtable's string has the class name as a suffix + // so why don't we use that rather than mallocing a string. + classname = symbolname(file, newClass.fVTableSym) + + sizeof(kVTablePrefix) - 1; + break_if(!addClass(file, &newClass, classname), + ("recordClass - no memory?\n")); + + return true; + } while (0); + + if (supername) + free(supername); + + return false; +} + +static Boolean getMetaClassGraph(struct fileRecord *file) +{ + const struct nlist *sym; + const char *strbase; + int i, nsyms; + + // Search the symbol table for the local symbols that are generated + // by the metaclass system. There are three metaclass variables + // that are relevant. + // + // .metaClass A pointer to the meta class structure. + // .superClass A pointer to the super class's meta class. + // .gMetaClass The meta class structure itself. + // ___vt The VTable for the class . + // + // In this code I'm going to search for any symbols that + // ends in kSuperClassSuffix as this indicates this class is a conforming + // OSObject subclass and will need to be patched, and it also + // contains a pointer to the super class's meta class structure. + strbase = file->fStringBase; + sym = file->fLocalSyms; + for (i = 0, nsyms = file->fNLocal; i < nsyms; i++, sym++) { + const char *symname; + const char *dot; + char classname[1024]; + unsigned char n_type = sym->n_type & (N_TYPE | N_EXT); + + // Check that the symbols is a global and that it has a name. + if (((N_SECT | N_EXT) != n_type && (N_ABS | N_EXT) != n_type) + || !sym->n_un.n_strx) + continue; + + // Only search from the last '.' in the symbol. + // but skip the leading '_' in all symbols first. + symname = strbase + sym->n_un.n_strx + 1; + dot = strrchr(symname, '.'); + if (!dot || strcmp(dot, kSuperClassSuffix)) + continue; + + // Got a candidate so hand it over for class processing. + return_if(dot - symname >= (int) sizeof(classname), + false, ("Symbol %s is too long\n", symname)); + + bcopy(symname, classname, dot - symname); + classname[dot - symname] = '\0'; + if (!recordClass(file, classname, sym)) + return false; + } + + return_if(!file->fClassList, false, ("Internal error, " + "getMetaClassGraph(%s) found no classes", file->fPath)); + + DEBUG_LOG(("Found %d classes in %x for %s\n", + DataGetLength(file->fClassList)/sizeof(void*), + file->fClassList, file->fPath)); + + return true; +} + +static Boolean mergeOSObjectsForFile(const struct fileRecord *file) +{ + int i, nmerged; + Boolean foundDuplicates = false; + +DEBUG_LOG(("Merging file %s\n", file->fPath)); // @@@ gvdl: + + if (!file->fClassList) + return true; + + if (!sMergedFiles) { + sMergedFiles = DataCreate(0); + return_if(!sMergedFiles, false, + ("Unable to allocate memory metaclass list\n", file->fPath)); + } + + // Check to see if we have already merged this file + nmerged = DataGetLength(sMergedFiles) / sizeof(struct fileRecord *); + for (i = 0; i < nmerged; i++) { + if (file == ((void **) DataGetPtr(sMergedFiles))[i]) + return true; + } + + if (!sMergeMetaClasses) { + sMergeMetaClasses = DataCreate(0); + return_if(!sMergeMetaClasses, false, + ("Unable to allocate memory metaclass list\n", file->fPath)); + } + else { /* perform a duplicate check */ + int i, j, cnt1, cnt2; + struct metaClassRecord **list1, **list2; + + list1 = (struct metaClassRecord **) DataGetPtr(file->fClassList); + cnt1 = DataGetLength(file->fClassList) / sizeof(*list1); + list2 = (struct metaClassRecord **) DataGetPtr(sMergeMetaClasses); + cnt2 = DataGetLength(sMergeMetaClasses) / sizeof(*list2); + + for (i = 0; i < cnt1; i++) { + for (j = 0; j < cnt2; j++) { + if (!strcmp(list1[i]->fClassName, list2[j]->fClassName)) { + errprintf("duplicate class %s in %s & %s\n", + list1[i]->fClassName, + file->fPath, list2[j]->fFile->fPath); + } + } + } + } + if (foundDuplicates) + return false; + + return_if(!DataAppendBytes(sMergedFiles, &file, sizeof(file)), false, + ("Unable to allocate memory to merge %s\n", file->fPath)); + + return_if(!DataAppendData(sMergeMetaClasses, file->fClassList), false, + ("Unable to allocate memory to merge %s\n", file->fPath)); + + if (file == sKernelFile) + sMergedKernel = true; + + return true; +} + +// Returns a pointer to the base of the section offset by the sections +// base address. The offset is so that we can add nlist::n_values directly +// to this address and get a valid pointer in our memory. +static unsigned char * +getSectionForSymbol(const struct fileRecord *file, const struct nlist *symb, + void ***endP) +{ + const struct section *section; + unsigned char sectind; + unsigned char *base; + + sectind = symb->n_sect; // Default to symbols section + if ((symb->n_type & N_TYPE) == N_ABS && file->fIsKernel) { + // Absolute symbol so we have to iterate over our sections + for (sectind = 1; sectind <= file->fNSects; sectind++) { + unsigned long start, end; + + section = file->fSections[sectind - 1].fSection; + start = section->addr; + end = start + section->size; + if (start <= symb->n_value && symb->n_value < end) { + // Found the relevant section + break; + } + } + } + + // Is the vtable in a valid section? + return_if(sectind == NO_SECT || sectind > file->fNSects, + (unsigned char *) -1, + ("%s isn't a valid kext, bad section reference\n", file->fPath)); + + section = file->fSections[sectind - 1].fSection; + + // for when we start walking the vtable so compute offset's now. + base = file->fMachO + section->offset; + *endP = (void **) (base + section->size); + + return base - section->addr; // return with addr offset +} + +static Boolean resolveKernelVTable(struct metaClassRecord *metaClass) +{ + const struct fileRecord *file; + struct patchRecord *patchedVTable; + void **curEntry, **vtableEntries, **endSection; + unsigned char *sectionBase; + struct patchRecord *curPatch; + int classSize; + + // Should never occur but it doesn't cost us anything to check. + if (metaClass->fPatchedVTable) + return true; + +DEBUG_LOG(("Kernel vtable %s\n", metaClass->fClassName)); // @@@ gvdl: + + // Do we have a valid vtable to patch? + return_if(!metaClass->fVTableSym, + false, ("Internal error - no class vtable symbol?\n")); + + file = metaClass->fFile; + + // If the metaClass we are being to ask is in the kernel then we + // need to do a quick scan to grab the fPatchList in a reliable format + // however we don't need to check the superclass in the kernel + // as the kernel vtables are always correct wrt themselves. + // Note this ends the superclass chain recursion. + return_if(!file->fIsKernel, + false, ("Internal error - resolveKernelVTable not kernel\n")); + + if (file->fNoKernelExecutable) { + // Oh dear attempt to map the kernel's VM into my memory space + return_if(file->fNoKernelExecutable, false, + ("Internal error - fNoKernelExecutable not implemented yet\n")); + } + + // We are going to need the base and the end + sectionBase = getSectionForSymbol(file, metaClass->fVTableSym, &endSection); + if (-1 == (long) sectionBase) + return false; + + vtableEntries = (void **) (sectionBase + metaClass->fVTableSym->n_value); + curEntry = vtableEntries + kVTablePreambleLen; + for (classSize = 0; curEntry < endSection && *curEntry; classSize++) + curEntry++; + + return_if(*curEntry, false, ("Bad kernel image, short section\n")); + + patchedVTable = (struct patchRecord *) + malloc((classSize + 1) * sizeof(struct patchRecord)); + return_if(!patchedVTable, false, ("resolveKernelVTable - no memory\n")); + + // Copy the vtable of this class into the patch table + curPatch = patchedVTable; + curEntry = vtableEntries + kVTablePreambleLen; + for (; *curEntry; curEntry++, curPatch++) { + curPatch->fSymbol = (struct nlist *) + findSymbolByAddress(file, *curEntry); + curPatch->fType = kSymbolLocal; + } + + // Tag the end of the patch vtable + curPatch->fSymbol = NULL; + metaClass->fPatchedVTable = patchedVTable; + + return true; +} + +// reloc->fPatch must contain a valid pointer on entry +static struct nlist * +getNewSymbol(struct fileRecord *file, + const struct relocRecord *reloc, const char *supername) +{ + unsigned int size, i, namelen; + struct nlist **sym; + struct nlist *msym; + const char *strbase; + struct relocation_info *rinfo; + long strx; + + if (!file->fNewSymbols) { + file->fNewSymbols = DataCreate(0); + return_if(!file->fNewSymbols, NULL, + ("Unable to allocate new symbol table for %s\n", file->fPath)); + } + + // Make sure we have a string table as well for the new symbol + if (!file->fNewStrings) { + file->fNewStrings = DataCreate(0); + return_if(!file->fNewStrings, NULL, + ("Unable to allocate string table for %s\n", file->fPath)); + } + + rinfo = (struct relocation_info *) reloc->fRInfo; + size = DataGetLength(file->fNewSymbols) / sizeof(struct nlist *); + sym = (const struct nlist **) DataGetPtr(file->fNewSymbols); + // remember that the n_strx for new symbols names is negated + strbase = (const char *) + DataGetPtr(file->fNewStrings) - file->fSymtab->strsize; + for (i = 0; i < size; i++, sym++) { + const char *symname = strbase - (*sym)->n_un.n_strx; + + if (!strcmp(symname, supername)) { + rinfo->r_symbolnum = i + file->fSymtab->nsyms; + file->fSymbolsDirty = true; + return *sym; + } + } + + // Assert that this is a vaild symbol. I need this condition to be true + // for the later code to make non-zero. So the first time through I'd + // better make sure that it is 0. + return_if(reloc->fSymbol->n_sect, NULL, + ("Undefined symbol entry with non-zero section %s:%s\n", + file->fPath, symbolname(file, reloc->fSymbol))); + + msym = (struct nlist *) malloc(sizeof(struct nlist)); + return_if(!msym, + NULL, ("Unable to create symbol table entry for %s\n", file->fPath)); + + // If we are here we didn't find the symbol so create a new one now + if (!DataAppendBytes(file->fNewSymbols, &msym, sizeof(msym))) { + free(msym); + return_if(true, + NULL, ("Unable to grow symbol table for %s\n", file->fPath)); + } + + namelen = strlen(supername) + 1; + strx = DataGetLength(file->fNewStrings); + if (!DataAppendBytes(file->fNewStrings, supername, namelen)) { + free(msym); + DataAddLength(file->fNewSymbols, -sizeof(struct nlist)); // Undo harm + return_if(true, NULL, + ("Unable to grow string table for %s\n", file->fPath)); + } + + // Offset the string index by the original string table size + // and negate the address to indicate that this is a 'new' symbol + msym->n_un.n_strx = -(strx + file->fSymtab->strsize); + msym->n_type = (N_EXT | N_UNDF); + msym->n_sect = NO_SECT; + msym->n_desc = 0; + msym->n_value = 0; + + // Mark the old symbol as being potentially deletable I can use the + // n_sect field as the input symbol must be of type N_UNDF which means + // that the n_sect field must be set to NO_SECT otherwise it is an + // in valid input file. + ((struct nlist *) reloc->fSymbol)->n_un.n_strx + = -reloc->fSymbol->n_un.n_strx; + ((struct nlist *) reloc->fSymbol)->n_sect = (unsigned char) -1; + + rinfo->r_symbolnum = i + file->fSymtab->nsyms; + file->fSymbolsDirty = true; + return msym; +} + +static struct nlist * +fixOldSymbol(struct fileRecord *file, + const struct relocRecord *reloc, const char *supername) +{ + unsigned int namelen; + struct nlist *sym = (struct nlist *) reloc->fSymbol; + const char *oldname = symbolname(file, sym); + + // assert(sym->n_un.n_strx >= 0); + + namelen = strlen(supername); + if (namelen < strlen(oldname)) { + // Overwrite old string in string table + strcpy((char *) oldname, supername); + } + else { + long strx; + + // Make sure we have a string table as well for this symbol + if (!file->fNewStrings) { + file->fNewStrings = DataCreate(0); + return_if(!file->fNewStrings, NULL, + ("Unable to allocate string table for %s\n", file->fPath)); + } + + // Find the end of the fNewStrings data structure; + strx = DataGetLength(file->fNewStrings); + return_if(!DataAppendBytes(file->fNewStrings, supername, namelen + 1), + NULL, ("Unable to grow string table for %s\n", file->fPath)); + + // now add the current table size to the offset + sym->n_un.n_strx = strx + file->fSymtab->strsize; + } + + // Mark the symbol as having been processed by negating it. + // Also note that we have dirtied the file and need to repair the + // symbol table. + sym->n_un.n_strx = -sym->n_un.n_strx; + file->fSymbolsDirty = true; + return sym; +} + +static enum patchState +symbolCompare(const struct fileRecord *file, + const struct nlist *classsym, + const char *supername) +{ + const char *classname; + + + // Check to see if the target function is locally defined + // if it is then we can assume this is a local vtable override + if ((classsym->n_type & N_TYPE) != N_UNDF) + return kSymbolLocal; + + // Check to see if both symbols point to the same symbol name + // if so then we are still identical. + classname = symbolname(file, classsym); + if (!strcmp(classname, supername)) + return kSymbolIdentical; + + // Right now we know that the target's vtable entry is different from the + // superclass' vtable entry. This means that we will have to apply a + // patch to the current entry, however before returning lets check to + // see if we have a _RESERVEDnnn field 'cause we can use this as a + // registration point that must align between vtables. + if (!strncmp(supername, kReservedPrefix, sizeof(kReservedPrefix) - 1)) + return kSymbolMismatch; + + // OK, we have a superclass difference where the superclass doesn't + // reference a pad function so assume that the superclass is correct. + if (!strncmp(classname, kReservedPrefix, sizeof(kReservedPrefix) - 1)) + return kSymbolPadUpdate; + else + return kSymbolSuperUpdate; +} + +static Boolean patchVTable(struct metaClassRecord *metaClass) +{ + struct metaClassRecord *super = NULL; + struct fileRecord *file; + struct patchRecord *patchedVTable; + struct relocRecord **curReloc, **vtableRelocs, **endSection; + unsigned char *sectionBase; + int classSize; + + // Should never occur but it doesn't cost us anything to check. + if (metaClass->fPatchedVTable) + return true; + + // Do we have a valid vtable to patch? + return_if(!metaClass->fVTableSym, + false, ("Internal error - no class vtable symbol?\n")); + + file = metaClass->fFile; + + // If the metaClass we are being to ask is in the kernel then we + // need to do a quick scan to grab the fPatchList in a reliable format + // however we don't need to check the superclass in the kernel + // as the kernel vtables are always correct wrt themselves. + // Note this ends the superclass chain recursion. + return_if(file->fIsKernel, + false, ("Internal error - patchVTable shouldn't used for kernel\n")); + + if (!metaClass->fSuperName) + return false; + + // The class isn't in the kernel so make sure that the super class + // is patched before patching ouselves. + super = getClass(sMergeMetaClasses, metaClass->fSuperName); + return_if(!super, false, ("Can't find superclass for %s : %s \n", + metaClass->fClassName, metaClass->fSuperName)); + + // Superclass recursion if necessary + if (!super->fPatchedVTable) { + Boolean res; + + if (super->fFile->fIsKernel) + res = resolveKernelVTable(super); + else + res = patchVTable(super); + if (!res) + return false; + } + +DEBUG_LOG(("Patching %s\n", metaClass->fClassName)); // @@@ gvdl: + + // We are going to need the base and the end + + sectionBase = getSectionForSymbol(file, + metaClass->fVTableSym, (void ***) &endSection); + if (-1 == (long) sectionBase) + return false; + + vtableRelocs = (struct relocRecord **) + (sectionBase + metaClass->fVTableSym->n_value); + curReloc = vtableRelocs + kVTablePreambleLen; + for (classSize = 0; curReloc < endSection && *curReloc; classSize++) + curReloc++; + + return_if(*curReloc, false, + ("%s isn't a valid kext, short section\n", file->fPath)); + + patchedVTable = (struct patchRecord *) + malloc((classSize + 1) * sizeof(struct patchRecord)); + return_if(!patchedVTable, false, ("patchedVTable - no memory\n")); + + do { + struct patchRecord *curPatch; + struct nlist *symbol; + + curPatch = patchedVTable; + curReloc = vtableRelocs + kVTablePreambleLen; + + // Grab the super table patches if necessary + // Can't be patching a kernel table as we don't walk super + // class chains in the kernel symbol space. + if (super && super->fPatchedVTable) { + const struct patchRecord *spp; + + spp = super->fPatchedVTable; + + for ( ; spp->fSymbol; curReloc++, spp++, curPatch++) { + const char *supername = + symbolname(super->fFile, spp->fSymbol); + + symbol = (struct nlist *) (*curReloc)->fSymbol; + + curPatch->fType = symbolCompare(file, symbol, supername); + switch (curPatch->fType) { + case kSymbolIdentical: + case kSymbolLocal: + break; + + case kSymbolSuperUpdate: + symbol = getNewSymbol(file, (*curReloc), supername); + break; + + case kSymbolPadUpdate: + symbol = fixOldSymbol(file, (*curReloc), supername); + break; + + case kSymbolMismatch: + errprintf("%s is not compatible with its %s superclass, " + "broken superclass?\n", + metaClass->fClassName, super->fClassName); + goto abortPatch; + + default: + errprintf("Internal error - unknown patch type\n"); + goto abortPatch; + } + if (symbol) { + curPatch->fSymbol = symbol; + (*curReloc)->fSymbol = symbol; + } + else + goto abortPatch; + } + } + + // Copy the remainder of this class' vtable into the patch table + for (; *curReloc; curReloc++, curPatch++) { + // Local reloc symbols + curPatch->fType = kSymbolLocal; + curPatch->fSymbol = (struct nlist *) (*curReloc)->fSymbol; + } + + // Tag the end of the patch vtable + curPatch->fSymbol = NULL; + + metaClass->fPatchedVTable = patchedVTable; + return true; + } while(0); + +abortPatch: + if (patchedVTable) + free(patchedVTable); + + return false; +} + +static Boolean growImage(struct fileRecord *file, vm_size_t delta) +{ +#if !KERNEL + file->fMachOSize += delta; + return (file->fMachO + file->fMachOSize <= file->fPadEnd); +#else /* KERNEL */ + vm_address_t startMachO, endMachO, endMap; + vm_offset_t newMachO; + vm_size_t newsize; + unsigned long i, nsect, nclass = 0; + struct metaClassRecord **classes = NULL; + struct sectionRecord *section; + kern_return_t ret; + + startMachO = (vm_address_t) file->fMachO; + endMachO = startMachO + file->fMachOSize + delta; + endMap = (vm_address_t) file->fMap + file->fMapSize; + + // Do we have room in the current mapped image + if (endMachO < round_page(endMap)) { + file->fMachOSize += delta; + return true; + } + + newsize = endMachO - startMachO; + if (newsize < round_page(file->fMapSize)) { + // We have room in the map if we shift the macho image within the + // current map. We will have to patch up pointers into the object. + newMachO = (vm_offset_t) file->fMap; + bcopy((char *) startMachO, (char *) newMachO, file->fMachOSize); + } + else if (file->fIsKmem) { + // kmem_alloced mapping so we can try a kmem_realloc + ret = kmem_realloc(kernel_map, + (vm_address_t) file->fMap, + (vm_size_t) file->fMapSize, + &newMachO, + newsize); + if (KERN_SUCCESS != ret) + return false; + + // If the mapping didn't move then just return + if ((vm_address_t) file->fMap == newMachO) { + file->fMachOSize = file->fMapSize = newsize; + return true; + } + + // We have relocated the kmem image so we are going to have to + // move all of the pointers into the image around. + } + else { + // The image doesn't have room for us and I can't kmem_realloc + // then I just have to bite the bullet and copy the object code + // into a bigger memory segment + ret = kmem_alloc(kernel_map, &newMachO, newsize); + + if (KERN_SUCCESS != ret) + return false; + bcopy((char *) startMachO, (void *) newMachO, file->fMachOSize); + file->fIsKmem = true; + } + + + file->fMap = file->fMachO = (unsigned char *) newMachO; + file->fMapSize = newsize; + file->fMachOSize += delta; // Increment the image size + + // If we are here then we have shifted the object image in memory + // I really should change all of my pointers into the image to machO offsets + // but I have run out of time. So I'm going to very quickly go over the + // cached data structures and add adjustments to the addresses that are + // affected. I wonder how long it will take me to get them all. + // + // For every pointer into the MachO I need to add an adjustment satisfying + // the following simultanous equations + // addr_old = macho_old + fixed_offset + // addr_new = macho_new + fixed_offset therefore: + // addr_new = addr_old + (macho_new - macho_old) +#define REBASE(addr, delta) ( ((vm_address_t) (addr)) += (delta) ) + delta = newMachO - startMachO; + + // Rebase the cached in object 'struct symtab_command' pointer + REBASE(file->fSymtab, delta); + + // Rebase the cached in object 'struct nlist' pointer for all symbols + REBASE(file->fSymbolBase, delta); + + // Rebase the cached in object 'struct nlist' pointer for local symbols + REBASE(file->fLocalSyms, delta); + + // Rebase the cached in object 'char' pointer for the string table + REBASE(file->fStringBase, delta); + + // Ok now we have to go over all of the relocs one last time + // to clean up the pad updates which had their string index negated + // to indicate that we have finished with them. + section = file->fSections; + for (i = 0, nsect = file->fNSects; i < nsect; i++, section++) + REBASE(section->fSection, delta); + + // We only ever grow images that contain class lists so dont bother + // the check if file->fClassList is non-zero 'cause it can't be + // assert(file->fClassList); + nclass = DataGetLength(file->fClassList) + / sizeof(struct metaClassRecord *); + classes = (struct metaClassRecord **) DataGetPtr(file->fClassList); + for (i = 0; i < nclass; i++) { + struct patchRecord *patch; + + for (patch = classes[i]->fPatchedVTable; patch->fSymbol; patch++) { + vm_address_t symAddr = (vm_address_t) patch->fSymbol; + if (symAddr >= startMachO && symAddr < endMachO) + REBASE(patch->fSymbol, delta); + } + } + + +#undef REBASE + + return true; + +#endif /* KERNEL */ +} + +static Boolean +prepareFileForLink(struct fileRecord *file) +{ + unsigned long i, last, numnewsyms, newsymsize, newstrsize; + struct sectionRecord *section; + struct nlist **symp, *sym; + + // If we didn't even do a pseudo 'relocate' and dirty the image + // then we can just return now. + if (!file->fImageDirty) + return true; + +DEBUG_LOG(("Linking 2 %s\n", file->fPath)); // @@@ gvdl: + + // We have to go over all of the relocs to repair the damage + // that we have done to the image when we did our 'relocation' + section = file->fSections; + for (i = 0, last = file->fNSects; i < last; i++, section++) { + unsigned char *sectionBase; + struct relocRecord *rec; + unsigned long j, nreloc; + + if (section->fRelocCache) { + sectionBase = file->fMachO + section->fSection->offset; + nreloc = section->fSection->nreloc; + rec = (struct relocRecord *) DataGetPtr(section->fRelocCache); + + // We will need to repair the reloc list + for (j = 0; j < nreloc; j++, rec++) { + void **entry; + struct nlist *sym; + + // Repair Damage to object image + entry = (void **) (sectionBase + rec->fRInfo->r_address); + *entry = rec->fValue; + + // Check if the symbol that this relocation entry points + // to is marked as erasable + sym = (struct nlist *) rec->fSymbol; + if (sym && sym->n_type == (N_EXT | N_UNDF) + && sym->n_sect == (unsigned char) -1) { + // clear mark now + sym->n_un.n_strx = -sym->n_un.n_strx; + sym->n_sect = NO_SECT; + } + } + + // Clean up the fRelocCache we don't need it any more. + DataRelease(section->fRelocCache); + section->fRelocCache = 0; + } + } + file->fImageDirty = false; // Image is clean + + // If we didn't dirty the symbol table then just return + if (!file->fSymbolsDirty) + return true; + + // calculate total file size increase and check against padding + numnewsyms = (file->fNewSymbols)? DataGetLength(file->fNewSymbols) : 0; + numnewsyms /= sizeof(struct nlist *); + newsymsize = numnewsyms * sizeof(struct nlist); + newstrsize = (file->fNewStrings)? DataGetLength(file->fNewStrings) : 0; + newstrsize = (newstrsize + 3) & ~3; // Round to nearest word + + return_if(!growImage(file, newsymsize + newstrsize), + false, ("Unable to patch the extension, no memory\n", file->fPath)); + + // Push out the new symbol table if necessary + if (numnewsyms) { + caddr_t base; + + // Move the string table out of the way of the grown symbol table + // Don't forget the '\0' from end of string table. + base = (caddr_t) file->fStringBase; + bcopy(base, base + newsymsize, file->fSymtab->strsize); + file->fStringBase += newsymsize; + file->fSymtab->stroff += newsymsize; + + // Now append the new symbols to the symbol table. + base = (caddr_t) file->fSymbolBase + + file->fSymtab->nsyms * sizeof(struct nlist); + symp = (struct nlist **) DataGetPtr(file->fNewSymbols); + for (i = 0; i < numnewsyms; i++, base += sizeof(struct nlist), symp++) + bcopy(*symp, base, sizeof(struct nlist)); + file->fSymtab->nsyms += numnewsyms; + + DataRelease(file->fNewSymbols); + file->fNewSymbols = 0; + } + + // Push out the new string table if necessary + if (newstrsize) { + caddr_t base = (caddr_t) file->fStringBase + file->fSymtab->strsize; + unsigned long actuallen = DataGetLength(file->fNewStrings); + + // Set the last word in string table to zero before copying data + *((unsigned long *) ((char *) base + newstrsize - 4)) = 0; + + // Now append the new strings to the end of the file + bcopy((caddr_t) DataGetPtr(file->fNewStrings), base, actuallen); + + file->fSymtab->strsize += newstrsize; + + DataRelease(file->fNewStrings); + file->fNewStrings = 0; + } + + // Repair the symbol table string index values + // I used negative strx's to indicate symbol has been processed + sym = file->fSymbolBase; + for (i = 0, last = file->fSymtab->nsyms; i < last; i++, sym++) { + if (sym->n_un.n_strx < 0) { + if ( sym->n_type != (N_EXT | N_UNDF) + || (unsigned char) -1 != sym->n_sect) + sym->n_un.n_strx = -sym->n_un.n_strx; + else { + // This symbol isn't being used by any vtable's reloc so + // convert it into an N_ABS style of symbol, remove the + // external bit and null out the symbol name. + bzero(sym, sizeof(*sym)); + sym->n_type = N_ABS; /* type flag, see below */ + } + } + } + file->fSymbolsDirty = false; + + return true; +} + +Boolean +#if KERNEL +kld_file_map(const char *pathName, + unsigned char *map, + size_t mapSize, + Boolean isKmem) +#else +kld_file_map(const char *pathName) +#endif /* KERNEL */ +{ + struct fileRecord file, *fp = 0; + + // Already done no need to repeat + fp = getFile(pathName); + if (fp) + return true; + + bzero(&file, sizeof(file)); + file.fPath = pathName; + +#if KERNEL + file.fMap = map; + file.fMapSize = mapSize; + file.fIsKmem = isKmem; +#else + if (!mapObjectFile(&file)) + return false; +#endif /* KERNEL */ + + do { + const struct machOMapping { + struct mach_header h; + struct load_command c[1]; + } *machO; + const struct load_command *cmd; + const struct nlist *sym; + unsigned int i, firstlocal, nsyms; + unsigned long strsize; + const char *strbase; + Boolean foundOSObject; + + if (!findBestArch(&file)) + break; + + machO = (const struct machOMapping *) file.fMachO; + if (file.fMachOSize < machO->h.sizeofcmds) + break; + + // If the file type is MH_EXECUTE then this must be a kernel + // as all Kernel extensions must be of type MH_OBJECT + for (i = 0, cmd = &machO->c[0]; i < machO->h.ncmds; i++) { + if (cmd->cmd == LC_SEGMENT) { + return_if(!parseSegments(&file, (struct segment_command *) cmd), + false, ("%s isn't a valid mach-o, bad segment\n", + file.fPath)); + } + else if (cmd->cmd == LC_SYMTAB) + file.fSymtab = (struct symtab_command *) cmd; + + cmd = (struct load_command *) ((UInt8 *) cmd + cmd->cmdsize); + } + break_if(!file.fSymtab, + ("%s isn't a valid mach-o, no symbols\n", file.fPath)); + + // we found a link edit segment so recompute the bases + if (file.fSymbolBase) { + struct segment_command *link = + (struct segment_command *) file.fSymbolBase; + + file.fSymbolBase = (struct nlist *) + (link->vmaddr + (file.fSymtab->symoff - link->fileoff)); + file.fStringBase = (char *) + (link->vmaddr + (file.fSymtab->stroff - link->fileoff)); + break_if( ( (caddr_t) file.fStringBase + file.fSymtab->strsize + > (caddr_t) link->vmaddr + link->vmsize ), + ("%s isn't a valid mach-o le, bad symbols\n", file.fPath)); + } + else { + file.fSymbolBase = (struct nlist *) + (file.fMachO + file.fSymtab->symoff); + file.fStringBase = (char *) + (file.fMachO + file.fSymtab->stroff); + break_if( ( file.fSymtab->stroff + file.fSymtab->strsize + > file.fMachOSize ), + ("%s isn't a valid mach-o, bad symbols\n", file.fPath)); + } + + // If this file the kernel and do we have an executable image + file.fIsKernel = (MH_EXECUTE == machO->h.filetype); + file.fNoKernelExecutable = (vm_page_size == file.fSymtab->symoff) + && (file.fSections[0].fSection->size == 0); + + // Search for the first non-stab symbol in table + strsize = file.fSymtab->strsize; + strbase = file.fStringBase; + sym = file.fSymbolBase; + firstlocal = 0; + foundOSObject = false; + for (i = 0, nsyms = file.fSymtab->nsyms; i < nsyms; i++, sym++) { + if ((unsigned long) sym->n_un.n_strx > strsize) + break; + + // Find the first exported symbol + if ( !file.fLocalSyms && (sym->n_type & N_EXT) ) { + file.fLocalSyms = sym; + firstlocal = i; + } + + // Find the a OSObject based subclass by searching for symbols + // that have a suffix of '.superClass' + if (!foundOSObject + && ((sym->n_type & (N_TYPE | N_EXT)) == (N_SECT | N_EXT) + || (sym->n_type & (N_TYPE | N_EXT)) == (N_ABS | N_EXT)) + && sym->n_un.n_strx) { + const char *dot; + + // Only search from the last '.' in the symbol. + // but skip the leading '_' in all symbols first. + dot = strrchr(strbase + sym->n_un.n_strx + 1, '.'); + if (dot && !strcmp(dot, kSuperClassSuffix)) + foundOSObject = true; + } + + // Find the last local symbol + if ( !file.fNLocal && sym->n_type == (N_EXT | N_UNDF) ) + file.fNLocal = i - firstlocal; + + } + break_if(i < nsyms, + ("%s isn't a valid mach-o, bad symbol strings\n", file.fPath)); + + break_if(!file.fLocalSyms, ("%s has no symbols?\n", file.fPath)); + + // If we don't have any undefined symbols then all symbols + // must be local so just compute it now if necessary. + if ( !file.fNLocal ) + file.fNLocal = i - firstlocal; + + fp = addFile(&file); + if (!fp) + break; + + if (foundOSObject && !getMetaClassGraph(fp)) + break; + + if (file.fIsKernel) + sKernelFile = fp; +#if KERNEL + if (!sKernelFile) { + extern struct mach_header _mh_execute_header; + extern struct segment_command *getsegbyname(char *seg_name); + + struct segment_command *sg; + size_t kernelSize; + Boolean ret; + + sg = (struct segment_command *) getsegbyname(kLinkEditSegName); + break_if(!sg, ("Can't find kernel link edit segment\n")); + + kernelSize = sg->vmaddr + sg->vmsize - (size_t) &_mh_execute_header; + ret = kld_file_map(kld_basefile_name, + (unsigned char *) &_mh_execute_header, kernelSize, + /* isKmem */ false); + break_if(!ret, ("kld can't map kernel file")); + } +#endif /* KERNEL */ + + return true; + } while(0); + + removeFile(&file); + + return false; +} + +void *kld_file_getaddr(const char *pathName, long *size) +{ + struct fileRecord *file = getFile(pathName); + + if (!file) + return 0; + + if (size) + *size = file->fMachOSize; + + return file->fMachO; +} + +void *kld_file_lookupsymbol(const char *pathName, const char *symname) +{ + struct fileRecord *file = getFile(pathName); + const struct nlist *sym; + const struct section *section; + unsigned char *sectionBase; + unsigned char sectind; + + return_if(!file, + NULL, ("Unknown file %s\n", pathName)); + + sym = findSymbolByName(file, symname); + + // May be a non-extern symbol so look for it there + if (!sym) { + const char *strbase; + unsigned int i, nsyms; + + sym = file->fSymbolBase; + strbase = file->fStringBase; + for (i = 0, nsyms = file->fSymtab->nsyms; i < nsyms; i++, sym++) { + if ( (sym->n_type & N_EXT) ) { + sym = 0; + break; // Terminate search when we hit an extern + } + if ( (sym->n_type & N_STAB) ) + continue; + if ( !strcmp(symname, strbase + sym->n_un.n_strx) ) + break; + } + } + + return_if(!sym, + NULL, ("Unknown symbol %s in %s\n", symname, pathName)); + + // Is the vtable in a valid section? + sectind = sym->n_sect; + return_if(sectind == NO_SECT || sectind > file->fNSects, NULL, + ("Malformed object file, invalid section reference for %s in %s\n", + symname, pathName)); + + section = file->fSections[sectind - 1].fSection; + sectionBase = file->fMachO + section->offset - section->addr; + + return (void *) (sectionBase + sym->n_value); +} + +Boolean kld_file_merge_OSObjects(const char *pathName) +{ + struct fileRecord *file = getFile(pathName); + + return_if(!file, + false, ("Internal error - unable to find file %s\n", pathName)); + + return mergeOSObjectsForFile(file); +} + +Boolean kld_file_patch_OSObjects(const char *pathName) +{ + struct fileRecord *file = getFile(pathName); + struct metaClassRecord **classes; + unsigned long i, last; + + return_if(!file, + false, ("Internal error - unable to find file %s\n", pathName)); + +DEBUG_LOG(("Patch file %s\n", pathName)); // @@@ gvdl: + + // If we don't have any classes we can return now. + if (!file->fClassList) + return true; + + // If we haven't alread merged the kernel then do it now + if (!sMergedKernel && sKernelFile) + mergeOSObjectsForFile(sKernelFile); + return_if(!sMergedKernel, false, ("Internal error no kernel?\n")); + + if (!mergeOSObjectsForFile(file)) + return false; + + // Patch all of the classes in this executable + last = DataGetLength(file->fClassList) / sizeof(void *); + classes = (struct metaClassRecord **) DataGetPtr(file->fClassList); + for (i = 0; i < last; i++) { + if (!patchVTable(classes[i])) + return false; + } + + return true; +} + +Boolean kld_file_prepare_for_link() +{ + if (sMergedFiles) { + unsigned long i, nmerged = 0; + struct fileRecord **files; + + // Check to see if we have already merged this file + nmerged = DataGetLength(sMergedFiles) / sizeof(struct fileRecord *); + files = (struct fileRecord **) DataGetPtr(sMergedFiles); + for (i = 0; i < nmerged; i++) { + if (!prepareFileForLink(files[i])) + return false; + } + } + + // Clear down the meta class table and merged file lists + DataRelease(sMergeMetaClasses); + DataRelease(sMergedFiles); + sMergedFiles = sMergeMetaClasses = NULL; + sMergedKernel = false; + + return true; +} + +void kld_file_cleanup_all_resources() +{ + unsigned long i, nfiles; + +#if KERNEL // @@@ gvdl: + // Debugger("kld_file_cleanup_all_resources"); +#endif + + if (!sFilesTable || !(nfiles = DataGetLength(sFilesTable))) + return; // Nothing to do just return now + + nfiles /= sizeof(struct fileRecord *); + for (i = 0; i < nfiles; i++) + removeFile(((void **) DataGetPtr(sFilesTable))[i]); + + // Don't really have to clean up anything more as the whole + // malloc engine is going to be released and I couldn't be bothered. +} + +#if !KERNEL +Boolean kld_file_debug_dump(const char *pathName, const char *outName) +{ + const struct fileRecord *file = getFile(pathName); + int fd; + Boolean ret = false; + + return_if(!file, false, ("Unknown file %s for dumping\n", pathName)); + + fd = open(outName, O_WRONLY|O_CREAT|O_TRUNC, 0666); + return_if(-1 == fd, false, ("Can't create output file %s - %s(%d)\n", + outName, strerror(errno), errno)); + + do { + break_if(-1 == write(fd, file->fMachO, file->fMachOSize), + ("Can't dump output file %s - %s(%d)\n", + outName, strerror(errno), errno)); + ret = true; + } while(0); + + close(fd); + + return ret; +} +#endif /* !KERNEL */ + diff --git a/kmodload.tproj/kld_patch.h b/kmodload.tproj/kld_patch.h new file mode 100644 index 0000000..b0e6058 --- /dev/null +++ b/kmodload.tproj/kld_patch.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * History: + * 2001-05-30 gvdl Initial implementation of the vtable patcher. + */ + +#include + +__BEGIN_DECLS + +#if KERNEL +extern Boolean kld_file_map(const char *pathName, + unsigned char *map, size_t mapSize, + Boolean isKmem); +#else +extern Boolean kld_file_map(const char *pathName); + +extern void * + kld_file_lookupsymbol(const char *pathName, const char *symbolname); + +Boolean kld_file_debug_dump(const char *pathName, const char *outName); +#endif /* KERNEL */ + +extern void *kld_file_getaddr(const char *pathName, long *size); + +extern Boolean kld_file_merge_OSObjects(const char *pathName); + +extern Boolean kld_file_patch_OSObjects(const char *pathName); + +extern Boolean kld_file_prepare_for_link(); + +extern void kld_file_cleanup_all_resources(); + +__END_DECLS diff --git a/kmodload.tproj/kmodload.c b/kmodload.tproj/kmodload.c index 4976944..4108282 100644 --- a/kmodload.tproj/kmodload.c +++ b/kmodload.tproj/kmodload.c @@ -49,10 +49,9 @@ * Original code from: * "kldload.c,v 1.5 1998/07/06 06:58:32 charnier Exp" */ - #ifndef lint static const char rcsid[] = - "$Id: kmodload.c,v 1.7 2001/02/05 19:53:16 lindak Exp $"; + "$Id: kmodload.c,v 1.10 2001/08/02 20:57:01 lindak Exp $"; #endif /* not lint */ #include @@ -63,6 +62,8 @@ static const char rcsid[] = #include #include #include +#include + #include #include @@ -71,12 +72,19 @@ static const char rcsid[] = #include #include +#include + +#include "kld_patch.h" + #define KMOD_ERROR_USAGE 1 #define KMOD_ERROR_PERMS 2 #define KMOD_ERROR_LOADING 3 #define KMOD_ERROR_INTERNAL 4 #define KMOD_ERROR_ALREADY 5 +#define kKMOD_INFO_SYMBOLNAME "_kmod_info" +#define kKmodsymsName "kmodsyms" + static mach_port_t kernel_port; static mach_port_t kernel_priv_port; @@ -96,26 +104,31 @@ static char *progname = "program name?"; static int kmodsyms = 0; static int link_addrs_set = 0; static int verbose = 0; -#define v_printf if (verbose) printf + +static char *debugdumpfile = NULL; // must not be static; kld library calls -void kld_error_vprintf(const char *format, va_list ap); +extern void kld_error_vprintf(const char *format, va_list ap); +static void e_printf(const char *fmt, ...); +static void v_printf(const char *fmt, ...); static void machwarn(int error, const char *message); static void macherr(int error, const char *message); -static unsigned long linkedit_address(unsigned long size, +static unsigned long linkedit_address(unsigned long size, unsigned long headers_size); -static void cleanup_kernel_memory(); -static void link_base(const char *base, +static void abort_load(int exitcode, const char *fmt, ...); +static void map_and_patch(const char *base, + const char **library_paths, + const char *module); +static void link_base(const char *base, const char **dependency_paths, const vm_address_t *dependency_addrs); static void clear_globals(void); -static void map_module(char *module_path, char **object_addr, - long *object_size, kmod_info_t **kinfo); -static struct mach_header *link_module(const char *filename, +static kmod_info_t *map_module(const char *filename); +static struct mach_header *link_module(const char *filename, const char *output); -static vm_address_t patch_module(struct mach_header *mach_header); +static vm_address_t update_kmod_info(struct mach_header *mach_header); static kmod_t load_module(struct mach_header *mach_header, vm_address_t info); static void set_module_dependencies(kmod_t id); @@ -125,11 +138,13 @@ static void usage(void) { if (kmodsyms) { - fprintf(stderr, "usage: kmodsyms [-v] [-k kernelfile] [-d dependencyfile] -o symbolfile modulefile\n"); - fprintf(stderr, " kmodsyms [-v] -k kernelfile [-d dependencyfile@address] -o symbolfile modulefile@address\n"); + fprintf(stderr, "usage: %s [-v] [-k kernelfile] [-d dependencyfile] -o symbolfile modulefile\n", progname); + fprintf(stderr, " %s [-v] -k kernelfile [-d dependencyfile@address] -o symbolfile modulefile@address\n", + progname); } else { - fprintf(stderr, "usage: kmodload [-v] [-k kernelfile] [-d dependencyfile] [-o symbolfile] modulefile\n"); + fprintf(stderr, "usage: %s [-v] [-k kernelfile] [-d dependencyfile] [-o symbolfile] modulefile\n", progname); } + fflush(stderr); exit(KMOD_ERROR_USAGE); } @@ -148,30 +163,28 @@ main(int argc, char** argv) char * module_path = ""; vm_address_t module_info = 0; - char *module_addr = 0; - long module_size = 0; vm_address_t module_faked_address = 0; kmod_t module_id = 0; kmod_info_t *file_kinfo; - if ((progname = rindex(argv[0], '/')) == NULL) + if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else ++progname; - kmodsyms = !strcmp(progname, "kmodsyms"); + kmodsyms = !strcmp(progname, kKmodsymsName); // XXX things to add: // -p data string to send as outofband data on start // -P data file to send as outofband data on start - while ((c = getopt(argc, argv, "d:o:k:v")) != -1) + while ((c = getopt(argc, argv, "D:d:o:k:v")) != -1) switch (c) { case 'd': dependencies[dependency_count] = optarg; if (kmodsyms) { char *address; - if ((address = rindex(optarg, '@'))) { + if ((address = strrchr(optarg, '@'))) { *address++ = 0; loaded_addresses[dependency_count] = strtoul(address, NULL, 0); link_addrs_set++; @@ -180,8 +193,8 @@ main(int argc, char** argv) } } if (++dependency_count == MAX_DEPENDANCIES) { - fprintf(stderr, "%s: internal error, dependency count overflow.\n", progname); - exit(KMOD_ERROR_INTERNAL); + abort_load(KMOD_ERROR_INTERNAL, + "internal error, dependency count overflow."); } break; case 'o': @@ -194,6 +207,9 @@ main(int argc, char** argv) case 'v': verbose = 1; break; + case 'D': + debugdumpfile = optarg; + break; default: usage(); } @@ -213,7 +229,7 @@ main(int argc, char** argv) if (!gdbfile) usage(); // check for @address - if ((address = rindex(module_path, '@'))) { + if ((address = strrchr(module_path, '@'))) { *address++ = 0; module_faked_address = strtoul(address, NULL, 0); link_addrs_set++; @@ -231,8 +247,8 @@ main(int argc, char** argv) } } - // map module and then check if it has been loaded - map_module(module_path, &module_addr, &module_size, &file_kinfo); + // map the module if possible, map_module will fail if there is a problem + file_kinfo = map_module(module_path); if (!link_addrs_set) { kmod_info_t *k; @@ -240,9 +256,9 @@ main(int argc, char** argv) // we only need the kernel port if we need to lookup loaded kmods r = task_for_pid(mach_task_self(), 0, &kernel_port); machwarn(r, "unable to get kernel task port"); - if (r) { - fprintf(stderr, "%s: You must be running as root to load/check modules in the kernel.\n", progname); - exit(KMOD_ERROR_PERMS); + if (KERN_SUCCESS != r) { + abort_load(KMOD_ERROR_PERMS, + "You must be running as root to load modules in the kernel."); } //get loaded modules @@ -254,8 +270,8 @@ main(int argc, char** argv) while (k) { if (!strcmp(k->name, file_kinfo->name)) { if (!kmodsyms) { - fprintf(stderr, "%s: the module named '%s' is already loaded.\n", progname, k->name); - exit(KMOD_ERROR_ALREADY); + abort_load(KMOD_ERROR_ALREADY, + "the module named '%s' is already loaded.", k->name); } else { module_faked_address = k->address; } @@ -265,14 +281,20 @@ main(int argc, char** argv) } if (kmodsyms && !module_faked_address) { - fprintf(stderr, "%s: the module named '%s' has not been loaded.\n", progname, file_kinfo->name); - exit(KMOD_ERROR_USAGE); + abort_load(KMOD_ERROR_USAGE, + "the module named '%s' has not been loaded.", file_kinfo->name); } //XXX it would be nice to be able to verify this is the correct kernel //XXX by comparing the kernel version strings (once we have them) } + map_and_patch(kernel, dependencies, module_path); + if (debugdumpfile) kld_file_debug_dump(module_path, debugdumpfile); + + // Tell the kld linker where to get its load address from + kld_address_func(linkedit_address); + // link the kernel along with any dependencies link_base(kernel, dependencies, loaded_addresses); @@ -280,13 +302,13 @@ main(int argc, char** argv) faked_kernel_load_address = module_faked_address; if (!faked_kernel_load_address) { - fprintf(stderr, "%s: internal error, fell thru without setting module load address.\n", progname); - exit(KMOD_ERROR_INTERNAL); + abort_load(KMOD_ERROR_INTERNAL, + "internal error, fell thru without setting module load address."); } } rld_header = link_module(module_path, gdbfile); - module_info = patch_module(rld_header); + module_info = update_kmod_info(rld_header); if (kmodsyms) return 0; @@ -303,123 +325,62 @@ main(int argc, char** argv) static void machwarn(int error, const char *message) { - if (error == KERN_SUCCESS) return; - fprintf(stderr, "%s: %s: %s\n", progname, message, mach_error_string(error)); + if (KERN_SUCCESS != error) + e_printf("%s: %s", message, mach_error_string(error)); } static void macherr(int error, const char *message) { - if (error == KERN_SUCCESS) return; - fprintf(stderr, "%s: %s: %s\n", progname, message, mach_error_string(error)); + if (KERN_SUCCESS != error) + abort_load(KMOD_ERROR_INTERNAL, + "%s: %s", message, mach_error_string(error)); +} + +static kmod_info_t *map_module(const char *filename) +{ + kmod_info_t *file_kinfo; - cleanup_kernel_memory(); + if (!kld_file_map(filename)) + exit(KMOD_ERROR_LOADING); - exit(KMOD_ERROR_INTERNAL); + file_kinfo = kld_file_lookupsymbol(filename, kKMOD_INFO_SYMBOLNAME); + if (!file_kinfo) { + abort_load(KMOD_ERROR_USAGE, + "%s is not a valid kernel module.", filename); + } + + return file_kinfo; } static void -map_module(char *module_path, char **object_addr, long *object_size, kmod_info_t **kinfo) +map_and_patch(const char *base, const char **library_paths, const char *module) { - int fd; - struct stat stat_buf; - struct mach_header *mh; - char *p; - - struct nlist nl[] = { - { "_kmod_info" }, - { "" }, - }; - - if((fd = open(module_path, O_RDONLY)) == -1){ - fprintf(stderr, "%s: Can't open: %s\n", progname, module_path); - exit(KMOD_ERROR_USAGE); - } - if (nlist(module_path, nl)) { - fprintf(stderr, "%s: %s is not a valid kernel module.\n", progname, module_path); - exit(KMOD_ERROR_USAGE); - } - if(fstat(fd, &stat_buf) == -1){ - fprintf(stderr, "%s: Can't stat file: %s\n", progname, module_path); - exit(KMOD_ERROR_PERMS); - } - *object_size = stat_buf.st_size; - if(map_fd(fd, 0, (vm_offset_t *)object_addr, TRUE, *object_size) != KERN_SUCCESS){ - fprintf(stderr, "%s: Can't map file: %s\n", progname, module_path); - exit(KMOD_ERROR_INTERNAL); - } - close(fd); - - if (NXSwapBigLongToHost(*((long *)*object_addr)) == FAT_MAGIC) { - struct host_basic_info hbi; - struct fat_header *fh; - struct fat_arch *fat_archs, *fap; - unsigned i, nfat_arch; - - /* Get our host info */ - i = HOST_BASIC_INFO_COUNT; - if (host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)(&hbi), &i) != KERN_SUCCESS) { - fprintf(stderr, "%s: Can't get host's basic info\n", progname); - exit(KMOD_ERROR_INTERNAL); - } - - // get number of architectures - fh = (struct fat_header *)*object_addr; - nfat_arch = NXSwapBigLongToHost(fh->nfat_arch); - - // find beginning of fat_arch struct - fat_archs = (struct fat_arch *)((char *)fh + sizeof(struct fat_header)); - - /* - * Convert archs to host byte ordering (a constraint of - * cpusubtype_getbestarch() - */ - for (i = 0; i < nfat_arch; i++) { - fat_archs[i].cputype = - NXSwapBigLongToHost(fat_archs[i].cputype); - fat_archs[i].cpusubtype = - NXSwapBigLongToHost(fat_archs[i].cpusubtype); - fat_archs[i].offset = - NXSwapBigLongToHost(fat_archs[i].offset); - fat_archs[i].size = - NXSwapBigLongToHost(fat_archs[i].size); - fat_archs[i].align = - NXSwapBigLongToHost(fat_archs[i].align); - } - -// this code was lifted from Darwin/Libraries/NeXT/libc/gen.subproj/nlist.c -// when cpusubtype_getbestarch exists this code should also be changed. -#define CPUSUBTYPE_SUPPORT 0 - -#if CPUSUBTYPE_SUPPORT - fap = cpusubtype_getbestarch(hbi.cpu_type, hbi.cpu_subtype, - fat_archs, nfat_arch); -#else CPUSUBTYPE_SUPPORT -#warning Use the cpusubtype functions!!! - fap = NULL; - for (i = 0; i < nfat_arch; i++) { - if (fat_archs[i].cputype == hbi.cpu_type) { - fap = &fat_archs[i]; - break; - } - } -#endif CPUSUBTYPE_SUPPORT - if (!fap) { - fprintf(stderr, "%s: could not find the correct architecture in %s.\n", progname, module_path); - exit(KMOD_ERROR_USAGE); + if (!kld_file_map(base)) + exit(KMOD_ERROR_INTERNAL); + if (!kld_file_merge_OSObjects(base)) + abort_load(KMOD_ERROR_LOADING, NULL); + + if (*library_paths) { + char **library; + for (library = library_paths; *library; library++) { + map_module(*library); + if (!kld_file_patch_OSObjects(*library)) + abort_load(KMOD_ERROR_LOADING, NULL); } - - *object_addr += fap->offset; - *object_size = fap->size; } - mh = (struct mach_header *)*object_addr; - if (*((long *)mh) != MH_MAGIC) { - fprintf(stderr, "%s: invalid file format for file: %s\n", progname, module_path); - exit(KMOD_ERROR_USAGE); - } - p = *object_addr + sizeof(struct mach_header) + mh->sizeofcmds + nl->n_value; - *kinfo = (kmod_info_t *)p; + // Patch the vtables of the object module we are about to load + // The module has already been mapped in the main() routine as part + // of validation + if (!kld_file_patch_OSObjects(module)) + abort_load(KMOD_ERROR_LOADING, NULL); + + // During the patch up process the mapped images were modified + // to avoid having to allocate more data than necessary. + // Now we have to give the patcher a chance to clean up after itself. + if (!kld_file_prepare_for_link()) + abort_load(KMOD_ERROR_LOADING, NULL); } static void @@ -428,25 +389,34 @@ link_base(const char *base, const vm_address_t *dependency_addrs) { struct mach_header *rld_header; + char *base_addr; + long base_size; int ok; - ok = kld_load_basefile(base); + // Get the address and size of the base, usually the kernel + base_addr = kld_file_getaddr(base, &base_size); + if (!base_addr) + exit(KMOD_ERROR_INTERNAL); // Error reported by kld library. + + ok = kld_load_basefile_from_memory(base, base_addr, base_size); fflush(stdout); - if (!ok) { - fprintf(stderr, "%s: kld_load_basefile(%s) failed.\n", progname, base); - exit(KMOD_ERROR_LOADING); - } + if (!ok) + abort_load(KMOD_ERROR_LOADING, "kld_load_basefile(%s) failed.", base); if (*dependency_paths) { char **dependency = dependency_paths; const vm_address_t *load_addr = dependency_addrs; while (*dependency) { - char *object_addr; - long object_size; kmod_info_t *file_kinfo; - map_module(*dependency, &object_addr, &object_size, &file_kinfo); + // Find the kmod_info structure in the image. + file_kinfo = + kld_file_lookupsymbol(*dependency, kKMOD_INFO_SYMBOLNAME); + if (!file_kinfo) { + abort_load(KMOD_ERROR_USAGE, + "%s is not a valid kernel module.", *dependency); + } // find the address that this dependency is loaded at if (kmodsyms && *load_addr) { @@ -461,11 +431,10 @@ link_base(const char *base, while (k) { if (!strcmp(k->name, file_kinfo->name)) { if (strcmp(k->version, file_kinfo->version)) { - fprintf(stderr, "%s: loaded kernel module '%s' version differs.\n", - progname, *dependency); - fprintf(stderr, "%s: loaded version '%s', file version '%s'.\n", - progname, k->version, file_kinfo->version); - exit(KMOD_ERROR_LOADING); + e_printf("loaded kernel module '%s' version differs.", *dependency); + abort_load(KMOD_ERROR_LOADING, + "loaded version '%s', file version '%s'.", + k->version, file_kinfo->version); } found_it++; break; @@ -473,17 +442,15 @@ link_base(const char *base, k = (k->next) ? (k + 1) : 0; } if (!found_it) { - fprintf(stderr, "%s: kernel module '%s' is not loaded.\n", - progname, *dependency); - exit(KMOD_ERROR_USAGE); + abort_load(KMOD_ERROR_USAGE, + "kernel module '%s' is not loaded.", *dependency); } - tmp = malloc(sizeof(kmod_info_t)); - if (!tmp) { - fprintf(stderr, "%s: no memory.\n", progname); - exit(KMOD_ERROR_LOADING); - } - *tmp = *k; + tmp = malloc(sizeof(kmod_info_t)); + if (!tmp) + abort_load(KMOD_ERROR_LOADING, "no memory."); + + *tmp = *k; tmp->next = module_dependencies; module_dependencies = tmp; @@ -492,7 +459,7 @@ link_base(const char *base, rld_header = link_module(*dependency, 0); - (void)patch_module(rld_header); + (void) update_kmod_info(rld_header); dependency++; load_addr++; } @@ -526,47 +493,28 @@ linkedit_address(unsigned long size, unsigned long headers_size) if (faked_kernel_load_address) { kernel_load_address = faked_kernel_load_address + kernel_hdr_pad; - v_printf("%s: Returning fake load address of 0x%8x\n", - progname, kernel_load_address); + v_printf("Returning fake load address of 0x%8x", kernel_load_address); return kernel_load_address; } if (kmodsyms) { - fprintf(stderr, "%s: internal error, almost tried to alloc kernel memory.\n", progname); - exit(KMOD_ERROR_INTERNAL); + abort_load(KMOD_ERROR_INTERNAL, + "internal error, almost tried to alloc kernel memory."); } r = vm_allocate(kernel_port, &kernel_alloc_address, kernel_alloc_size, TRUE); macherr(r, "unable to allocate kernel memory"); - v_printf("%s: allocated %ld bytes in kernel space at 0x%8x\n", - progname, kernel_alloc_size, kernel_alloc_address); + v_printf("allocated %ld bytes in kernel space at 0x%8x", + kernel_alloc_size, kernel_alloc_address); kernel_load_address = kernel_alloc_address + kernel_hdr_pad; - v_printf("%s: Returning load address of 0x%x\n", - progname, kernel_load_address); + v_printf("Returning load address of 0x%x", kernel_load_address); return kernel_load_address; } -static void -cleanup_kernel_memory() -{ - int r; - - if (faked_kernel_load_address) return; - - if (kernel_alloc_address || kernel_alloc_size) { - v_printf("%s: freeing %ld bytes in kernel space at 0x%x\n", - progname, kernel_alloc_size, kernel_alloc_address); - r = vm_deallocate(kernel_port, kernel_alloc_address, kernel_alloc_size); - clear_globals(); - kernel_load_address = kernel_load_size = 0; - machwarn(r, "unable to cleanup kernel memory"); - } -} - static void clear_globals(void) { @@ -584,51 +532,50 @@ static struct mach_header * link_module(const char *filename, const char *output) { struct mach_header *rld_header; + char *object_addr; + long object_size; int ok; - kld_address_func(linkedit_address); + // Get the address of the thined MachO image. + object_addr = kld_file_getaddr(filename, &object_size); + if (!object_addr) + abort_load(KMOD_ERROR_LOADING, NULL); - ok = kld_load(&rld_header, filename, output); + ok = kld_load_from_memory(&rld_header, filename, + object_addr, object_size, output); fflush(stdout); - if (!ok) { - fprintf(stderr, "%s: kld_load() failed.\n", progname); - cleanup_kernel_memory(); - exit(KMOD_ERROR_LOADING); - } + if (!ok) + abort_load(KMOD_ERROR_LOADING, "kld_load() failed."); return rld_header; } +// Update the kmod_info_t structure in the image to be laoded +// Side effect of removing the kKMOD_INFO_SYMBOLNAME from the +// loaded symbol name space, otherwise we would have a duplicate +// defined symbol failure vm_address_t -patch_module(struct mach_header *mach_header) +update_kmod_info(struct mach_header *mach_header) { - char * symbol = "_kmod_info"; + char * symbol = kKMOD_INFO_SYMBOLNAME; kmod_info_t *info; unsigned long value; int ok; - ok = kld_lookup(symbol, &value); - fflush(stdout); - if (!ok) { - fprintf(stderr, "%s: kld_lookup(%s) failed.\n", progname, symbol); - cleanup_kernel_memory(); - exit(KMOD_ERROR_LOADING); - } + ok = kld_lookup(symbol, &value); fflush(stdout); + if (!ok) + abort_load(KMOD_ERROR_LOADING, "kld_lookup(%s) failed.", symbol); - ok = kld_forget_symbol(symbol); - fflush(stdout); - if (!ok) { - fprintf(stderr, "%s: kld_forget_symbol(%s) failed.\n", progname, symbol); - cleanup_kernel_memory(); - exit(KMOD_ERROR_INTERNAL); - } + ok = kld_forget_symbol(symbol); fflush(stdout); + if (!ok) + abort_load(KMOD_ERROR_LOADING, "kld_forget_symbol(%s) failed.", symbol); /* Get the kmod info by translating from the kernel address at value. */ info = (kmod_info_t *)(value - (unsigned long)kernel_load_address + (unsigned long)mach_header); - v_printf("%s: kmod name: %s\n", progname, info->name); - v_printf("%s: kmod start @ 0x%x\n", progname, (vm_address_t)info->start); - v_printf("%s: kmod stop @ 0x%x\n", progname, (vm_address_t)info->stop); + v_printf("kmod name: %s", info->name); + v_printf("kmod start @ 0x%x", (vm_address_t)info->start); + v_printf("kmod stop @ 0x%x", (vm_address_t)info->stop); /* Record link info in kmod info struct, rounding the hdr_size to fit * the adjustment that was made. @@ -637,16 +584,10 @@ patch_module(struct mach_header *mach_header) info->size = kernel_alloc_size; info->hdr_size = page_round(kernel_hdr_size); - if (!info->start) { - fprintf(stderr, "%s: invalid start address?\n", progname); - cleanup_kernel_memory(); - exit(KMOD_ERROR_LOADING); - } - if (!info->stop) { - fprintf(stderr, "%s: invalid stop address?\n", progname); - cleanup_kernel_memory(); - exit(KMOD_ERROR_LOADING); - } + if (!info->start) + abort_load(KMOD_ERROR_LOADING, "invalid start address?"); + else if (!info->stop) + abort_load(KMOD_ERROR_LOADING, "invalid stop address?"); return (vm_address_t)value; } @@ -680,8 +621,8 @@ load_module(struct mach_header *mach_header, vm_address_t info) r = kmod_create(kernel_priv_port, info, &id); macherr(r, "unable to register module with kernel"); - v_printf("%s: kmod id %d successfully created at 0x%x size %ld.\n", - progname, id, kernel_alloc_address, kernel_alloc_size); + v_printf("kmod id %d successfully created at 0x%x size %ld.\n", + id, kernel_alloc_address, kernel_alloc_size); // FIXME: make sure this happens even on failure @@ -705,10 +646,9 @@ set_module_dependencies(kmod_t id) clear_globals(); r = kmod_destroy(kernel_priv_port, id); macherr(r, "kmod_destroy failed"); - exit(KMOD_ERROR_INTERNAL); } - v_printf("%s: kmod id %d reference count was sucessfully incremented.\n", progname, module->id); + v_printf("kmod id %d reference count was sucessfully incremented.", module->id); module = module->next; } @@ -727,14 +667,64 @@ start_module(kmod_t id) clear_globals(); kmod_destroy(kernel_priv_port, id); macherr(r, "kmod_destroy failed"); - exit(KMOD_ERROR_INTERNAL); } - v_printf("%s: kmod id %d successfully started.\n", progname, id); + v_printf("kmod id %d successfully started.", id); } -void -kld_error_vprintf(const char *format, va_list ap){ - vfprintf(stderr, format, ap); - return; +static void e_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + kld_error_vprintf(fmt, ap); + va_end(ap); +} + +static void v_printf(const char *fmt, ...) +{ + va_list ap; + char msg[1024]; + + if (!verbose) return; + + va_start(ap, fmt); + vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + printf("%s: %s\n", progname, msg); +} + +static void abort_load(int exitcode, const char *fmt, ...) +{ + if (fmt) { + va_list ap; + + va_start(ap, fmt); + kld_error_vprintf(fmt, ap); + va_end(ap); + } + + if (!faked_kernel_load_address + && (kernel_alloc_address || kernel_alloc_size)) { + int r; + + v_printf("freeing %ld bytes in kernel space at 0x%x", + kernel_alloc_size, kernel_alloc_address); + r = vm_deallocate(kernel_port, kernel_alloc_address, kernel_alloc_size); + machwarn(r, "unable to cleanup kernel memory"); + } + + exit(exitcode); +} + +__private_extern__ void +kld_error_vprintf(const char *fmt, va_list ap) +{ + char msg[1024]; + + vsnprintf(msg, sizeof(msg), fmt, ap); + fprintf(stderr, "%s: %s", progname, msg); + + fflush(stderr); } diff --git a/latency.tproj/latency.c b/latency.tproj/latency.c index 16ef9e7..fc2cf35 100644 --- a/latency.tproj/latency.c +++ b/latency.tproj/latency.c @@ -62,8 +62,8 @@ #include #include #include -#include #include +#include #include @@ -145,15 +145,18 @@ struct ct { char name[32]; } codes_tab[MAX_ENTRIES]; +/* If NUMPARMS changes from the kernel, then PATHLENGTH will also reflect the change */ +#define NUMPARMS 23 +#define PATHLENGTH (NUMPARMS*sizeof(long)) struct th_info { int thread; int type; int child_thread; - int vfslookup; int arg1; double stime; - char pathname[32]; + long *pathptr; + char pathname[PATHLENGTH + 1]; }; #define MAX_THREADS 512 @@ -202,40 +205,6 @@ static kern_return_t set_standard_policy(void); int decrementer_val = 0; /* Value used to reset decrementer */ int set_remove_flag = 1; /* By default, remove trace buffer */ -/* raw read of the timebase register */ -void clock_get_uptime( register AbsoluteTime *result) -{ -#ifdef __ppc__ - - register UInt32 hic; - do { - asm volatile(" mftbu %0" : "=r" (result->hi)); - asm volatile(" mftb %0" : "=r" (result->lo)); - asm volatile(" mftbu %0" : "=r" (hic)); - } while (hic != result->hi); - -#else - result->lo = 0; - result->hi = 0; -#endif /* __ppc__ */ - -} - -typedef unsigned long long abstime_scalar_t; - -#define AbsoluteTime_to_scalar(x) \ - (*(abstime_scalar_t *)(x)) - -/* t1 += t2 */ -#define ADD_ABSOLUTETIME(t1, t2) \ - (AbsoluteTime_to_scalar(t1) += \ - AbsoluteTime_to_scalar(t2)) - -/* t1 -= t2 */ -#define SUB_ABSOLUTETIME(t1, t2) \ - (AbsoluteTime_to_scalar(t1) -= \ - AbsoluteTime_to_scalar(t2)) - int quit(s) char *s; @@ -717,10 +686,10 @@ int argc; char *argv[]; { mach_timespec_t remain; - unsigned long long start, stop; - AbsoluteTime timestamp1; - AbsoluteTime timestamp2; - AbsoluteTime adeadline, adelay; + uint64_t start, stop; + uint64_t timestamp1; + uint64_t timestamp2; + uint64_t adeadline, adelay; double fdelay; int elapsed_usecs; double nanosecs_to_sleep; @@ -825,7 +794,7 @@ char *argv[]; nanosecs_to_sleep = (double)(num_of_usecs_to_sleep * 1000); fdelay = nanosecs_to_sleep * (divisor /1000); - AbsoluteTime_to_scalar(&adelay) = (abstime_scalar_t)fdelay; + adelay = (uint64_t)fdelay; init_code_file(); @@ -890,17 +859,14 @@ char *argv[]; refresh_time = curr_time + 1; } - clock_get_uptime(×tamp1); - adeadline = timestamp1; - ADD_ABSOLUTETIME(&adeadline, &adelay); + timestamp1 = mach_absolute_time(); + adeadline = timestamp1 + adelay; mk_wait_until(adeadline); - clock_get_uptime(×tamp2); + timestamp2 = mach_absolute_time(); - start = (((unsigned long long)timestamp1.hi) << 32) | - (unsigned long long)((unsigned int)(timestamp1.lo)); + start = timestamp1; - stop = (((unsigned long long)timestamp2.hi) << 32) | - (unsigned long long)((unsigned int)(timestamp2.lo)); + stop = timestamp2; elapsed_usecs = (int)(((double)(stop - start)) / divisor); @@ -945,23 +911,17 @@ char *argv[]; gotSIGWINCH = 0; } } - } - +} void getdivisor() { + mach_timebase_info_data_t info; - unsigned int delta; - unsigned int abs_to_ns_num; - unsigned int abs_to_ns_denom; - unsigned int proc_to_abs_num; - unsigned int proc_to_abs_denom; + (void) mach_timebase_info (&info); - (void)MKGetTimeBaseInfo (&delta, &abs_to_ns_num, &abs_to_ns_denom, - &proc_to_abs_num, &proc_to_abs_denom); + divisor = ( (double)info.denom / (double)info.numer) * 1000; - divisor = ((double)abs_to_ns_denom / (double)abs_to_ns_num) * 1000; } /* This is the realtime band */ @@ -1097,8 +1057,13 @@ void create_map_entry(int thread, char *command) #endif map->valid = 1; map->thread = thread; - (void)strncpy (map->command, command, sizeof(map->command)); - map->command[sizeof(map->command)-1] = '\0'; + /* + The trace entry that returns the command name will hold + at most, MAXCOMLEN chars, and in that case, is not + guaranteed to be null terminated. + */ + (void)strncpy (map->command, command, MAXCOMLEN); + map->command[MAXCOMLEN] = '\0'; } @@ -1168,10 +1133,10 @@ char *find_code(type) } -void sample_sc(long long start, long long stop) +void sample_sc(uint64_t start, uint64_t stop) { kd_buf *kd, *last_mach_sched, *last_decrementer_kd, *start_kd, *end_of_sample; - unsigned long long now; + uint64_t now; int count; int first_entry = 1; char command[32]; @@ -1211,7 +1176,7 @@ void sample_sc(long long start, long long stop) for (i = 0; i < cur_max; i++) { th_state[i].thread = 0; th_state[i].type = -1; - th_state[i].vfslookup = 0; + th_state[i].pathptr = (long *)0; th_state[i].pathname[0] = 0; } cur_max = 0; @@ -1237,6 +1202,7 @@ void sample_sc(long long start, long long stop) for (kd = (kd_buf *)my_buffer; kd < end_of_sample; kd++) { int debugid, thread, cpunum; int type, clen, mode; + int len; char *p; long *sargptr; double i_latency; @@ -1263,8 +1229,8 @@ void sample_sc(long long start, long long stop) if (type == DECR_TRAP) i_latency = handle_decrementer(kd); - now = (((unsigned long long)kd->timestamp.tv_sec) << 32) | - (unsigned long long)((unsigned int)(kd->timestamp.tv_nsec)); + now = (((uint64_t)kd->timestamp.tv_sec) << 32) | + (uint64_t)((unsigned int)(kd->timestamp.tv_nsec)); timestamp = ((double)now) / divisor; @@ -1432,32 +1398,48 @@ void sample_sc(long long start, long long stop) ti->thread = thread; ti->type = -1; - ti->vfslookup = 0; + ti->pathptr = (long *)0; ti->child_thread = 0; } - if (ti->vfslookup == 0) { - ti->vfslookup = 1; + if (!ti->pathptr) { ti->arg1 = kd->arg1; - memset(&ti->pathname[0], 0, 32); + memset(&ti->pathname[0], 0, (PATHLENGTH + 1)); sargptr = (long *)&ti->pathname[0]; *sargptr++ = kd->arg2; *sargptr++ = kd->arg3; *sargptr++ = kd->arg4; - - } else if (ti->vfslookup == 1) { - ti->vfslookup = 0; - - sargptr = (long *)&ti->pathname[12]; - *sargptr++ = kd->arg1; - *sargptr++ = kd->arg2; - *sargptr++ = kd->arg3; - *sargptr++ = kd->arg4; + ti->pathptr = sargptr; + + } else { + sargptr = ti->pathptr; + + /* + We don't want to overrun our pathname buffer if the + kernel sends us more VFS_LOOKUP entries than we can + handle. + */ + + if ((long *)sargptr < (long *)&ti->pathname[PATHLENGTH]) + { + *sargptr++ = kd->arg1; + *sargptr++ = kd->arg2; + *sargptr++ = kd->arg3; + *sargptr++ = kd->arg4; + ti->pathptr = sargptr; + + /* print the tail end of the pathname */ + len = strlen(ti->pathname); + if (len > 28) + len -= 28; + else + len = 0; - if (log_fp) { - fprintf(log_fp, "%9.1f %8.1f\t\t%-28.28s %-28s %-8x %-8x %d %s\n", - timestamp - start_bias, delta, "VFS_LOOKUP", - ti->pathname, ti->arg1, thread, cpunum, command); + if (log_fp) { + fprintf(log_fp, "%9.1f %8.1f\t\t%-28.28s %-28s %-8x %-8x %d %s\n", + timestamp - start_bias, delta, "VFS_LOOKUP", + &ti->pathname[len], ti->arg1, thread, cpunum, command); + } } } last_timestamp = timestamp; @@ -1554,7 +1536,8 @@ enter_syscall(FILE *fp, kd_buf *kd, int thread, int type, char *command, double else ti->type = -1; ti->stime = timestamp; - ti->vfslookup = 0; + ti->pathptr = (long *)0; + #if 0 if (print_info && fp) fprintf(fp, "cur_max = %d, ti = %x, type = %x, thread = %x\n", cur_max, ti, ti->type, ti->thread); @@ -1607,7 +1590,7 @@ exit_syscall(FILE *fp, kd_buf *kd, int thread, int type, char *command, double t ti->thread = thread; ti->child_thread = 0; - ti->vfslookup = 0; + ti->pathptr = (long *)0; } } ti->type = -1; @@ -1654,7 +1637,7 @@ check_for_thread_update(int thread, int type, kd_buf *kd) ti->thread = thread; ti->type = -1; - ti->vfslookup = 0; + ti->pathptr = (long *)0; } ti->child_thread = kd->arg1; return (1); @@ -1684,7 +1667,8 @@ kd_buf *log_decrementer(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, d double timestamp, last_timestamp, delta, start_bias; int thread, cpunum; int debugid, type, clen; - unsigned long long now; + int len; + uint64_t now; struct th_info *ti; long *sargptr; char *p; @@ -1721,8 +1705,8 @@ kd_buf *log_decrementer(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, d if (kd_stop >= end_of_sample) kd_stop = end_of_sample - 1; - now = (((unsigned long long)kd_start->timestamp.tv_sec) << 32) | - (unsigned long long)((unsigned int)(kd_start->timestamp.tv_nsec)); + now = (((uint64_t)kd_start->timestamp.tv_sec) << 32) | + (uint64_t)((unsigned int)(kd_start->timestamp.tv_nsec)); timestamp = ((double)now) / divisor; for (kd = kd_start; kd <= kd_stop; kd++) { @@ -1741,8 +1725,8 @@ kd_buf *log_decrementer(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, d debugid = kd->debugid; type = kd->debugid & DBG_FUNC_MASK; - now = (((unsigned long long)kd->timestamp.tv_sec) << 32) | - (unsigned long long)((unsigned int)(kd->timestamp.tv_nsec)); + now = (((uint64_t)kd->timestamp.tv_sec) << 32) | + (uint64_t)((unsigned int)(kd->timestamp.tv_nsec)); timestamp = ((double)now) / divisor; @@ -1838,31 +1822,46 @@ kd_buf *log_decrementer(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, d ti->thread = thread; ti->type = -1; - ti->vfslookup = 0; + ti->pathptr = (long *)0; ti->child_thread = 0; } - if (ti->vfslookup == 0) { - ti->vfslookup = 1; + if (!ti->pathptr) { ti->arg1 = kd->arg1; - memset(&ti->pathname[0], 0, 32); + memset(&ti->pathname[0], 0, (PATHLENGTH + 1)); sargptr = (long *)&ti->pathname[0]; *sargptr++ = kd->arg2; *sargptr++ = kd->arg3; *sargptr++ = kd->arg4; - - } else if (ti->vfslookup == 1) { - ti->vfslookup = 0; - - sargptr = (long *)&ti->pathname[12]; - *sargptr++ = kd->arg1; - *sargptr++ = kd->arg2; - *sargptr++ = kd->arg3; - *sargptr++ = kd->arg4; + ti->pathptr = sargptr; + + } else { + sargptr = ti->pathptr; + + /* + We don't want to overrun our pathname buffer if the + kernel sends us more VFS_LOOKUP entries than we can + handle. + */ + + if ((long *)sargptr < (long *)&ti->pathname[PATHLENGTH]) + { + *sargptr++ = kd->arg1; + *sargptr++ = kd->arg2; + *sargptr++ = kd->arg3; + *sargptr++ = kd->arg4; + + /* print the tail end of the pathname */ + len = strlen(ti->pathname); + if (len > 28) + len -= 28; + else + len = 0; - fprintf(log_fp, "%9.1f %8.1f\t\t%-28.28s %-28s %-8x %-8x %d %s\n", - timestamp - start_bias, delta, "VFS_LOOKUP", - ti->pathname, ti->arg1, thread, cpunum, command); + fprintf(log_fp, "%9.1f %8.1f\t\t%-28.28s %-28s %-8x %-8x %d %s\n", + timestamp - start_bias, delta, "VFS_LOOKUP", + &ti->pathname[len], ti->arg1, thread, cpunum, command); + } } last_timestamp = timestamp; break; diff --git a/login.tproj/login.c b/login.tproj/login.c index 9a48977..cbfd719 100644 --- a/login.tproj/login.c +++ b/login.tproj/login.c @@ -364,6 +364,7 @@ main(argc, argv) (void)chown(ttyn, pwd->pw_uid, (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); + (void)chmod(ttyn, 0620); (void)setgid(pwd->pw_gid); initgroups(username, pwd->pw_gid); diff --git a/nvram.tproj/Makefile b/nvram.tproj/Makefile index aed70c0..debd283 100644 --- a/nvram.tproj/Makefile +++ b/nvram.tproj/Makefile @@ -26,7 +26,7 @@ DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) -FRAMEWORKS = -framework IOKit +FRAMEWORKS = -framework CoreFoundation -framework IOKit NEXTSTEP_BUILD_OUTPUT_DIR = /$(USER)/BUILD diff --git a/nvram.tproj/PB.project b/nvram.tproj/PB.project index 25536c7..3184bf4 100644 --- a/nvram.tproj/PB.project +++ b/nvram.tproj/PB.project @@ -2,7 +2,7 @@ DYNAMIC_CODE_GEN = YES; FILESTABLE = { BUNDLES = (); - FRAMEWORKS = (IOKit.framework); + FRAMEWORKS = (CoreFoundation.framework, IOKit.framework); H_FILES = (); LIBS = (); OTHER_LINKED = (nvram.c); diff --git a/sc_usage.tproj/sc_usage.c b/sc_usage.tproj/sc_usage.c index 86ed491..5198d9a 100644 --- a/sc_usage.tproj/sc_usage.c +++ b/sc_usage.tproj/sc_usage.c @@ -56,7 +56,7 @@ cc -I. -DKERNEL_PRIVATE -O -o sc_usage sc_usage.c #include #include -#import +#include #include @@ -89,6 +89,9 @@ long start_time = 0; #define MAX_NESTED 8 #define MAX_FAULTS 5 +/* If NUMPARMS from kernel changes, it will be reflected in PATHLENGTH as well */ +#define NUMPARMS 23 +#define PATHLENGTH (NUMPARMS*sizeof(long)) char *state_name[] = { "Dont Know", @@ -104,12 +107,6 @@ char *state_name[] = { #define WAITING 3 #define PREEMPTED 4 -typedef struct { - natural_t hi; - natural_t lo; -} AbsoluteTime; - - struct entry { int sc_state; int type; @@ -125,7 +122,8 @@ struct th_info { int depth; int vfslookup; int curpri; - char pathname[32]; + long *pathptr; + char pathname[PATHLENGTH + 1]; struct entry th_entry[MAX_NESTED]; }; @@ -266,29 +264,6 @@ void sigwinch() newLINES = 1; } - -/* raw read of the timebase register */ -void clock_get_uptime( - AbsoluteTime *result) -{ -#ifdef __ppc__ - - natural_t hi, lo, hic; - - do { - asm volatile(" mftbu %0" : "=r" (hi)); - asm volatile(" mftb %0" : "=r" (lo)); - asm volatile(" mftbu %0" : "=r" (hic)); - } while (hic != hi); - - result->lo = lo; - result->hi = hi; -#else - result->lo = 0; - result->hi = 0; -#endif /* __ppc__ */ -} - int exit_usage(myname) { @@ -600,6 +575,7 @@ void screen_update() char *p1, *p2, *p3; char tbuf[256]; int clen; + int plen; int n, i, rows; long curr_time; long elapsed_secs; @@ -851,7 +827,7 @@ void screen_update() printf("\n"); if (num_of_threads) { - sprintf(tbuf, "\nCURRENT_TYPE LAST_PATHNAME_WAITED_FOR CUR_WAIT_TIME THRD# PRI\n"); + sprintf(tbuf, "\nCURRENT_TYPE LAST_PATHNAME_WAITED_FOR CUR_WAIT_TIME THRD# PRI\n"); if (tbuf[COLS-2] != '\n') { tbuf[COLS-1] = '\n'; @@ -876,14 +852,10 @@ void screen_update() for (i = 0; i < num_of_threads; i++, ti++) { struct entry *te; - unsigned long long now; - AbsoluteTime timestamp; + uint64_t now; int secs, time_secs, time_usecs; - clock_get_uptime(×tamp); - - now = (((unsigned long long)timestamp.hi) << 32) | - (unsigned long long)((unsigned int)(timestamp.lo)); + now = mach_absolute_time(); while (ti->thread == 0 && ti < &th_state[MAX_THREADS]) ti++; @@ -906,7 +878,13 @@ void screen_update() } clen = strlen(tbuf); - sprintf(&tbuf[clen], " %-28.28s ", ti->pathname); + /* print the tail end of the pathname */ + plen = strlen(ti->pathname); + if (plen > 34) + plen -= 34; + else + plen = 0; + sprintf(&tbuf[clen], " %-34.34s ", &ti->pathname[plen]); clen += strlen(&tbuf[clen]); @@ -917,7 +895,7 @@ void screen_update() print_time(&tbuf[clen], time_usecs, time_secs); clen += strlen(&tbuf[clen]); - sprintf(&tbuf[clen], " %2d %3d\n", i, ti->curpri); + sprintf(&tbuf[clen], " %2d %3d\n", i, ti->curpri); if (tbuf[COLS-2] != '\n') { tbuf[COLS-1] = '\n'; @@ -1175,13 +1153,9 @@ sort_scalls() { struct th_info *ti; struct sc_entry *se; struct entry *te; - unsigned long long now; - AbsoluteTime timestamp; - - clock_get_uptime(×tamp); + uint64_t now; - now = (((unsigned long long)timestamp.hi) << 32) | - (unsigned long long)((unsigned int)(timestamp.lo)); + now = mach_absolute_time(); for (ti = th_state; ti < &th_state[MAX_THREADS]; ti++) { if (ti->thread == 0) @@ -1215,6 +1189,7 @@ sort_scalls() { if ((unsigned long)(((double)now - te->otime) / divisor) > 5000000) { ti->thread = 0; ti->vfslookup = 0; + ti->pathptr = (long *)0; ti->pathname[0] = 0; num_of_threads--; } @@ -1417,6 +1392,7 @@ sample_sc() th_state[i].depth = 0; th_state[i].thread = 0; th_state[i].vfslookup = 0; + th_state[i].pathptr = (long *)0; th_state[i].pathname[0] = 0; } num_of_threads = 0; @@ -1432,7 +1408,7 @@ sample_sc() for (i = 0; i < count; i++) { int debugid, baseid, thread; int type, code; - unsigned long long now; + uint64_t now; struct th_info *ti, *switched_out, *switched_in; struct sc_entry *se; struct entry *te; @@ -1445,8 +1421,8 @@ sample_sc() switched_out = (struct th_info *)0; switched_in = (struct th_info *)0; - now = (((unsigned long long)kd[i].timestamp.tv_sec) << 32) | - (unsigned long long)((unsigned int)(kd[i].timestamp.tv_nsec)); + now = (((uint64_t)kd[i].timestamp.tv_sec) << 32) | + (uint64_t)((unsigned int)(kd[i].timestamp.tv_nsec)); baseid = debugid & 0xffff0000; if (debugid == vfs_lookup) { @@ -1455,22 +1431,32 @@ sample_sc() if ((ti = find_thread(thread)) == (struct th_info *)0) continue; if (ti->vfslookup == 1) { - ti->vfslookup = 2; - memset(&ti->pathname[0], 0, 32); - sargptr = (long *)&ti->pathname[0]; - + ti->vfslookup++; + memset(&ti->pathname[0], 0, (PATHLENGTH + 1)); + sargptr = (long *)&ti->pathname[0]; + *sargptr++ = kd[i].arg2; *sargptr++ = kd[i].arg3; *sargptr++ = kd[i].arg4; + ti->pathptr = sargptr; + + } else if (ti->vfslookup > 1) { + ti->vfslookup++; + sargptr = ti->pathptr; - } else if (ti->vfslookup == 2) { - ti->vfslookup = 3; - - sargptr = (long *)&ti->pathname[12]; + /* + We don't want to overrun our pathname buffer if the + kernel sends us more VFS_LOOKUP entries than we can + handle. + */ + + if ((long *)sargptr >= (long *)&ti->pathname[PATHLENGTH]) + continue; *sargptr++ = kd[i].arg1; *sargptr++ = kd[i].arg2; *sargptr++ = kd[i].arg3; *sargptr++ = kd[i].arg4; + ti->pathptr = sargptr; } continue; @@ -1617,7 +1603,7 @@ sample_sc() se->delta_count++; se->total_count++; - if (ti->depth) { + while (ti->depth) { te = &ti->th_entry[ti->depth-1]; if (te->type == type) { @@ -1653,6 +1639,20 @@ sample_sc() te->stime = (double)now; te->otime = (double)now; + + break; + } + ti->depth--; + + if (ti->depth == 0) { + /* + * headed back to user mode + * start the time accumulation + */ + te = &ti->th_entry[0]; + te->sc_state = USER_MODE; + te->stime = (double)now; + te->otime = (double)now; } } } @@ -1699,20 +1699,14 @@ char *s; exit(1); } - void getdivisor() { + mach_timebase_info_data_t info; - unsigned int delta; - unsigned int abs_to_ns_num; - unsigned int abs_to_ns_denom; - unsigned int proc_to_abs_num; - unsigned int proc_to_abs_denom; + (void) mach_timebase_info (&info); - MKGetTimeBaseInfo (&delta, &abs_to_ns_num, &abs_to_ns_denom, - &proc_to_abs_num, &proc_to_abs_denom); + divisor = ( (double)info.denom / (double)info.numer) * 1000; - divisor = ((double)abs_to_ns_denom / (double)abs_to_ns_num) * 1000; } diff --git a/sc_usage.tproj/trace.codes b/sc_usage.tproj/trace.codes index 8a46599..25331b5 100644 --- a/sc_usage.tproj/trace.codes +++ b/sc_usage.tproj/trace.codes @@ -1,4 +1,4 @@ -772 +773 0xff000104 MSG_mach_notify_port_deleted 0xff000114 MSG_mach_notify_port_destroyed 0xff000118 MSG_mach_notify_no_senders @@ -20,7 +20,7 @@ 0xff000354 MSG_processor_set_default 0xff000358 MSG_processor_set_create 0xff00035c MSG_mach_memory_object_memory_entry_64 -0xff000360 MSG_host_statistics +0xff000360 MSG_host_statistics 0xff000640 MSG_host_get_boot_info 0xff000644 MSG_host_reboot 0xff000648 MSG_host_priv_statistics @@ -56,31 +56,26 @@ 0xff001f40 MSG_memory_object_get_attributes 0xff001f44 MSG_memory_object_change_attributes 0xff001f48 MSG_memory_object_synchronize_completed -0xff001f50 MSG_memory_object_lock_request -0xff001f58 MSG_memory_object_destroy -0xff001f64 MSG_vm_object_upl_request -0xff001f68 MSG_vm_pager_upl_request -0xff001f6c MSG_vm_upl_map -0xff001f70 MSG_vm_upl_unmap -0xff001f74 MSG_vm_upl_abort -0xff001f78 MSG_vm_upl_commit -0xff001f7c MSG_vm_upl_commit_range -0xff002008 MSG_vm_object_upl_request -0xff00200c MSG_vm_pager_upl_request -0xff002010 MSG_vm_upl_abort -0xff002014 MSG_vm_upl_abort_range -0xff002018 MSG_vm_upl_commit -0xff00201c MSG_vm_upl_commit_range +0xff001f4c MSG_memory_object_lock_request +0xff001f50 MSG_memory_object_destroy +0xff001f54 MSG_memory_object_upl_request +0xff001f58 MSG_memory_object_super_upl_request +0xff001f5c MSG_memory_object_page_op +0xff001f60 MSG_memory_object_recover_named +0xff001f64 MSG_memory_object_release_name +0xff002008 MSG_upl_abort +0xff00200c MSG_upl_abort_range +0xff002010 MSG_upl_commit +0xff002014 MSG_upl_commit_range 0xff002260 MSG_memory_object_init 0xff002264 MSG_memory_object_terminate 0xff002268 MSG_memory_object_data_request -0xff00226c MSG_memory_object_data_unlock -0xff002270 MSG_memory_object_lock_completed -0xff002278 MSG_memory_object_data_return -0xff00227c MSG_memory_object_synchronize -0xff002280 MSG_memory_object_change_completed +0xff00226c MSG_memory_object_data_return +0xff002270 MSG_memory_object_data_initialize +0xff002274 MSG_memory_object_data_unlock +0xff002278 MSG_memory_object_synchronize +0xff00227c MSG_memory_object_unmap 0xff002328 MSG_memory_object_create -0xff00232c MSG_memory_object_data_initialize 0xff00238c MSG_default_pager_object_create 0xff002390 MSG_default_pager_info 0xff002394 MSG_default_pager_objects @@ -99,14 +94,14 @@ 0xff002bc8 MSG_io_iterator_next 0xff002bcc MSG_io_iterator_reset 0xff002bd0 MSG_io_service_get_matching_services -0xff002bd4 MSG_io_service_add_notification_old +0xff002bd4 MSG_io_registry_entry_get_property 0xff002bd8 MSG_io_registry_create_iterator 0xff002bdc MSG_io_registry_iterator_enter_entry 0xff002be0 MSG_io_registry_iterator_exit_entry 0xff002be4 MSG_io_registry_entry_from_path 0xff002be8 MSG_io_registry_entry_get_name 0xff002bec MSG_io_registry_entry_get_properties -0xff002bf0 MSG_io_registry_entry_get_property +0xff002bf0 MSG_io_registry_entry_get_property_bytes 0xff002bf4 MSG_io_registry_entry_get_child_iterator 0xff002bf8 MSG_io_registry_entry_get_parent_iterator 0xff002bfc MSG_io_service_open @@ -148,6 +143,8 @@ 0xff002c8c MSG_io_service_acknowledge_notification 0xff002c90 MSG_io_connect_get_notification_semaphore 0xff002c94 MSG_io_connect_unmap_memory +0xff002c98 MSG_io_registry_entry_get_location_in_plane +0xff002c9c MSG_io_registry_entry_get_property_recursively 0xff002ee0 MSG_processor_start 0xff002ee4 MSG_processor_exit 0xff002ee8 MSG_processor_info @@ -268,6 +265,9 @@ 0xff003bc0 MSG_vm_region_64 0xff003bc4 MSG_mach_make_memory_entry_64 0xff003bc8 MSG_vm_map_64 +0xff003bcc MSG_vm_map_get_upl +0xff003bd0 MSG_vm_upl_map +0xff003bd4 MSG_vm_upl_unmap 0xff003e80 MSG_processor_set_statistics 0xff003e84 MSG_processor_set_destroy 0xff003e88 MSG_processor_set_max_priority @@ -305,6 +305,7 @@ 0x1300000 MACH_vmfault 0x1400000 MACH_SCHED 0x1400008 MACH_STKHANDOFF +0x1400018 MACH_MKRUNNABLE 0x1500000 MACH_MSGID_INVALID 0x1090000 DecrTrap 0x1090004 DecrSet @@ -397,8 +398,8 @@ 0x10c0158 MSC_kern_invalid_#86 0x10c015c MSC_kern_invalid_#87 0x10c0160 MSC_kern_invalid_#88 -0x10c0164 MSC_kern_invalid_#89 -0x10c0168 MSC_mk_wait_until +0x10c0164 MSC_mach_timebase_info +0x10c0168 MSC_mach_wait_until 0x10c016c MSC_mk_timer_create 0x10c0170 MSC_mk_timer_destroy 0x10c0174 MSC_mk_timer_arm diff --git a/top.tproj/Makefile b/top.tproj/Makefile index 8dbfac9..aa66116 100644 --- a/top.tproj/Makefile +++ b/top.tproj/Makefile @@ -28,7 +28,7 @@ DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) -FRAMEWORKS = -framework IOKit +FRAMEWORKS = -framework CoreFoundation -framework IOKit NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc diff --git a/top.tproj/PB.project b/top.tproj/PB.project index b1fcde6..8068b70 100644 --- a/top.tproj/PB.project +++ b/top.tproj/PB.project @@ -1,7 +1,7 @@ { DYNAMIC_CODE_GEN = YES; FILESTABLE = { - FRAMEWORKS = (IOKit.framework); + FRAMEWORKS = (CoreFoundation.framework, IOKit.framework); H_FILES = (); OTHER_LINKED = (top.c); OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble, top.1); diff --git a/top.tproj/top.c b/top.tproj/top.c index 3907ed8..d4dd628 100644 --- a/top.tproj/top.c +++ b/top.tproj/top.c @@ -445,17 +445,6 @@ pmem_doit(task_port_t task, int pid, int *shared, int *private, int *aliased, in mach_msg_type_number_t count; vm_size_t size; - if ( !split && (address >= FW_CODE_BEG_ADDR && address < FW_DATA_END_ADDR)) { - vm_region_basic_info_data_64_t b_info; - - count = VM_REGION_BASIC_INFO_COUNT_64; - if (err = vm_region_64(task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&b_info, - &count, &object_name)) - break; - - if (b_info.reserved) - split = 1; - } count = VM_REGION_TOP_INFO_COUNT; if (err = vm_region(task, &address, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info, @@ -466,6 +455,17 @@ pmem_doit(task_port_t task, int pid, int *shared, int *private, int *aliased, in *fw_private += info.private_pages_resident * vm_page_size; + if ( !split && info.share_mode == SM_EMPTY) { + vm_region_basic_info_data_64_t b_info; + + count = VM_REGION_BASIC_INFO_COUNT_64; + if (err = vm_region_64(task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&b_info, + &count, &object_name)) + break; + + if (b_info.reserved) + split = 1; + } if (info.share_mode != SM_PRIVATE) { address += size; continue; @@ -830,20 +830,23 @@ void get_proc_info(kpb, pi) (void) vm_deallocate(mach_task_self(), (vm_offset_t)thread_table, table_size * sizeof(*thread_table)); - if (mach_port_names(task, &names, &ncnt, - &types, &tcnt) == KERN_SUCCESS) { - pi->num_ports = ncnt; - pi->orig_num_ports = ncnt; - (void) vm_deallocate(mach_task_self(), - (vm_offset_t) names, - ncnt * sizeof(*names)); - (void) vm_deallocate(mach_task_self(), - (vm_offset_t) types, - tcnt * sizeof(*types)); - } else { - pi->num_ports = -1; - } - + if (!events_only) { + if (mach_port_names(task, &names, &ncnt, + &types, &tcnt) == KERN_SUCCESS) { + pi->num_ports = ncnt; + pi->orig_num_ports = ncnt; + (void) vm_deallocate(mach_task_self(), + (vm_offset_t) names, + ncnt * sizeof(*names)); + (void) vm_deallocate(mach_task_self(), + (vm_offset_t) types, + tcnt * sizeof(*types)); + } else { + pi->num_ports = -1; + } + } else + pi->num_ports = 0; + if (events_only) { task_events_info_data_t tei; @@ -1202,6 +1205,7 @@ main(argc, argv) char *argv[]; { char *myname = "top"; + int ch; int delay = Default_DELAY; kern_return_t error; @@ -1225,19 +1229,11 @@ main(argc, argv) events_delta = 0; events_accumulate = 0; - while (argc > 1 && argv[1][0] == '-') { - switch (argv[1][1]) { + while ((ch = getopt(argc, argv, "uwks:edal:")) != EOF) { + switch(ch) { case 's': - if (argv[1][2]) - delay = atoi(&argv[1][2]); - else { - argc--; - argv++; - - if (argc > 1) - delay = atoi(&argv[1][0]); - } - break; + delay = atoi(optarg); + break; case 'u': sort_by_usage = 1; break; @@ -1259,21 +1255,13 @@ main(argc, argv) events_accumulate = 1; break; case 'l': - if (argv[1][2]) - logcnt = atoi(&argv[1][2]); - else { - argc--; - argv++; - - if (argc > 1) - logcnt = atoi(&argv[1][0]); - } + logcnt = atoi(optarg); oneshot = 1; LINES = 80; COLS = 132; break; default: - fprintf(stderr, "Usage: %s [-u] [-w] [-k] [-sn] [-e] [-d] [number]\n", myname); + fprintf(stderr, "Usage: %s [-u] [-w] [-k] [-sn] [-e | -d | -a] [-ln] [number]\n", myname); fprintf(stderr, " -u enables sort by usage\n"); fprintf(stderr, " -w enables wide output of additional info\n"); fprintf(stderr, " -k generate vm info for kernel(proc 0)... expensive\n"); @@ -1286,9 +1274,11 @@ main(argc, argv) exit(1); } - argc--; - argv++; } + + argc -= optind; + //argv += optind; + if (events_only) { if ( wide_output || do_proc0_vm) @@ -1303,8 +1293,8 @@ main(argc, argv) host_port = get_host_port(); /* get count of top processes to display (if any) */ - if (argc > 1) { - wanted_topn = topn = atoi(argv[1]); + if (argc) { + wanted_topn = topn = atoi(argv[optind]); } else wanted_topn = -1; @@ -1448,7 +1438,7 @@ void screen_update() unsigned long long total_virtual_size; unsigned long long total_private_size; unsigned long long total_shared_size; - unsigned int total_memory_regions; + unsigned int total_memory_regions = 0; unsigned int total_shared_objects; unsigned int total_fw_code_size; unsigned int total_fw_data_size; @@ -1492,9 +1482,11 @@ void screen_update() mach_error("host_info", error); exit(EXIT_FAILURE); } - getNETWORKcounters(); - getDISKcounters(); - + + if (events_only) { + getNETWORKcounters(); + getDISKcounters(); + } /* count up process states and get pointers to interesting procs */ mpid = 0; @@ -1746,7 +1738,7 @@ void screen_update() i_kbytes = i_net.kbytes; o_kbytes = o_net.kbytes; } - sprintf(tbuf, "en0: %10d ipkts/%dK", i_io, i_kbytes); + sprintf(tbuf, "Networks:%10d ipkts/%dK", i_io, i_kbytes); clen = strlen(tbuf); memset(&tbuf[clen], ' ', 36 - clen); sprintf(&tbuf[36], "%10d opkts /%dK\n", o_io, o_kbytes); @@ -1792,7 +1784,7 @@ void screen_update() i_kbytes = i_dsk.kbytes; o_kbytes = o_dsk.kbytes; } - sprintf(tbuf, "Disks: %10d reads/%dK", i_io, i_kbytes); + sprintf(tbuf, "Disks: %10d reads/%dK", i_io, i_kbytes); clen = strlen(tbuf); memset(&tbuf[clen], ' ', 36 - clen); sprintf(&tbuf[36], "%10d writes/%dK\n", o_io, o_kbytes); @@ -1835,7 +1827,7 @@ void screen_update() pageins = vm_stat.pageins; pageouts = vm_stat.pageouts; } - sprintf(tbuf, "VM: %10d pageins", pageins); + sprintf(tbuf, "VM: %10d pageins", pageins); clen = strlen(tbuf); memset(&tbuf[clen], ' ', 36 - clen); sprintf(&tbuf[36], "%10d pageouts\n", pageouts); @@ -2199,25 +2191,26 @@ getNETWORKcounters() if (kread(nl_net[N_IFNET].n_value, (char *)&ifnethead, sizeof ifnethead)) return; + i_net.io = 0; + o_net.io = 0; + + i_net.kbytes = 0; + o_net.kbytes = 0; + for (off = (u_long)ifnethead.tqh_first; off; ) { - char name[16], tname[16]; + char tname[16]; if (kread(off, (char *)&ifnet, sizeof ifnet)) break; if (kread((u_long)ifnet.if_name, tname, 16)) break; - tname[15] = '\0'; - snprintf(name, 16, "%s%d", tname, ifnet.if_unit); - - if (strcmp(name, "en0") == 0) { - i_net.io = ifnet.if_ipackets; - o_net.io = ifnet.if_opackets; + if (strncmp(tname, "lo", 2)) { + i_net.io += ifnet.if_ipackets; + o_net.io += ifnet.if_opackets; - i_net.kbytes = ifnet.if_ibytes/1024; - o_net.kbytes = ifnet.if_obytes/1024; - - return; + i_net.kbytes += ifnet.if_ibytes/1024; + o_net.kbytes += ifnet.if_obytes/1024; } off = (u_long) ifnet.if_link.tqe_next; } -- 2.47.2