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,
off_t filesize = size;
char subfile[512];
FILE *file_ptr;
- kern_return_t error;
file_count = 0;
sprintf(subfile, "%s%d", fileroot, file_count);
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(),
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,
.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
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;
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
int num_of_pids = 0;
int exclude_pids = 0;
+int exclude_default_pids = 1;
struct kinfo_proc *kp_buffer = 0;
int kp_nentries = 0;
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);
}
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);
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]);
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++)
{
kd_buf *kd;
int i, count;
+ size_t needed;
void read_command_map();
void create_map_entry();
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;
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;
}
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) {
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;
ti->arg2 = kd->arg2;
ti->arg3 = kd->arg3;
ti->arg4 = kd->arg4;
+ ti->pathptr = (long *)0;
ti->pathname[0] = 0;
break;
default:
break;
}
+ fflush (0);
}
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;
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;
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
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';
}
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);
#if TIMERSOURCE
static void _KEXTDTimerCallout(CFRunLoopTimerRef timer, void * info)
{
- KEXTDScanPaths((KEXTDRef)info);
+ KEXTDScanPaths((KEXTDRef)info, false);
}
#endif
// 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.
k = (KEXTD *)info;
-// KEXTDScanPaths((KEXTDRef)k);
+// KEXTDScanPaths((KEXTDRef)k, false);
PTLockTakeLock(k->_queue_lock);
while ( !queue_empty(&k->_requestQ) ) {
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];
if ( k->_manager )
KEXTManagerReset(k->_manager);
- KEXTDScanPaths(kextd);
+ KEXTDScanPaths(kextd, false);
}
static KEXTReturn _KEXTDSendDataToCatalog(KEXTDRef kextd, int flag, CFTypeRef obj)
if ( CFArrayGetCount(toload) > 0 ) {
error = KEXTManagerLoadPersonalities(((KEXTD *)kextd)->_manager, toload);
- } else {
- KEXTdaemonSignal();
}
+ KEXTdaemonSignal();
+
CFRelease(toload);
return error;
static void * _KEXTDKmodWait(void * info)
{
mach_port_t kmodPort;
- kern_return_t kr;
KEXTD * kextd;
KEXTReturn error;
request_t * reqstruct;
return;
signalled = TRUE;
if (gDebug) {
- printf("kextd: idle\n");
+ printf("kextd: signal\n");
return;
}
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
#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;
};
gDebug = debug;
- if (!debug) {
+ if (!debug && !cdMKextBoot) {
errno = 0;
KEXTdaemon(0, 0);
if ( errno ) {
_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) {
syslog(LOG_INFO, "started.");
IOCatalogueReset(k->_catPort, kIOCatalogResetDefault);
- KEXTDScanPaths(kextd);
+ KEXTDScanPaths(kextd, false);
#if TIMERSOURCE
if ( poll ) {
return kKEXTReturnSuccess;
}
-void KEXTDScanPaths(KEXTDRef kextd)
+void KEXTDScanPaths(KEXTDRef kextd, Boolean cdMKextBoot)
{
KEXTReturn error;
KEXTD * k;
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 {
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);
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);
}
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];
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;
}
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();
}
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 ) {
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);
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
{
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);
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;
PROF_LIBS = $(LIBS)
-FRAMEWORKS = -framework IOKit
+FRAMEWORKS = -framework CoreFoundation -framework IOKit
NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
{
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 = ();
PROF_LIBS = $(LIBS)
-FRAMEWORKS = -framework IOKit
+FRAMEWORKS = -framework CoreFoundation -framework IOKit
NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
{
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);
};
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
PROF_LIBS = $(LIBS)
-HEADER_PATHS =\
- -I$(NEXT_ROOT)/System/Library/Frameworks/System.framework/PrivateHeaders
NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
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;
--- /dev/null
+/*
+ * 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 <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/reloc.h>
+
+#if KERNEL
+
+#include <stdarg.h>
+#include <string.h>
+
+#include <sys/systm.h>
+
+#include <libkern/OSTypes.h>
+
+#include <libsa/stdlib.h>
+#include <libsa/mach/mach.h>
+
+#include "mach_loader.h"
+
+#include <vm/vm_kern.h>
+
+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 <unistd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <mach-o/arch.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#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 <cname>.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.
+ //
+ // <ClassName>.metaClass A pointer to the meta class structure.
+ // <ClassName>.superClass A pointer to the super class's meta class.
+ // <ClassName>.gMetaClass The meta class structure itself.
+ // ___vt<ClassName> The VTable for the class <ClassName>.
+ //
+ // 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 */
+
--- /dev/null
+/*
+ * 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 <sys/cdefs.h>
+
+__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
* 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 <stdlib.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
+#include <sys/mman.h>
+
#include <paths.h>
#include <mach/mach.h>
#include <mach-o/kld.h>
#include <mach-o/fat.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#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;
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);
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);
}
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++;
}
}
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':
case 'v':
verbose = 1;
break;
+ case 'D':
+ debugdumpfile = optarg;
+ break;
default:
usage();
}
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++;
}
}
- // 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;
// 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
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;
}
}
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);
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;
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
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) {
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;
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;
rld_header = link_module(*dependency, 0);
- (void)patch_module(rld_header);
+ (void) update_kmod_info(rld_header);
dependency++; load_addr++;
}
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)
{
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.
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;
}
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
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;
}
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);
}
#include <mach/mach_types.h>
#include <mach/message.h>
#include <mach/mach_syscalls.h>
-#include <mach/clock.h>
#include <mach/clock_types.h>
+#include <mach/mach_time.h>
#include <libkern/OSTypes.h>
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
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;
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;
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();
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);
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 */
#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';
}
}
-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];
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;
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;
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;
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;
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);
ti->thread = thread;
ti->child_thread = 0;
- ti->vfslookup = 0;
+ ti->pathptr = (long *)0;
}
}
ti->type = -1;
ti->thread = thread;
ti->type = -1;
- ti->vfslookup = 0;
+ ti->pathptr = (long *)0;
}
ti->child_thread = kd->arg1;
return (1);
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;
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++) {
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;
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;
(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);
PROF_LIBS = $(LIBS)
-FRAMEWORKS = -framework IOKit
+FRAMEWORKS = -framework CoreFoundation -framework IOKit
NEXTSTEP_BUILD_OUTPUT_DIR = /$(USER)/BUILD
DYNAMIC_CODE_GEN = YES;
FILESTABLE = {
BUNDLES = ();
- FRAMEWORKS = (IOKit.framework);
+ FRAMEWORKS = (CoreFoundation.framework, IOKit.framework);
H_FILES = ();
LIBS = ();
OTHER_LINKED = (nvram.c);
#include <sys/sysctl.h>
#include <errno.h>
-#import <mach/clock_types.h>
+#include <mach/mach_time.h>
#include <err.h>
#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",
#define WAITING 3
#define PREEMPTED 4
-typedef struct {
- natural_t hi;
- natural_t lo;
-} AbsoluteTime;
-
-
struct entry {
int sc_state;
int type;
int depth;
int vfslookup;
int curpri;
- char pathname[32];
+ long *pathptr;
+ char pathname[PATHLENGTH + 1];
struct entry th_entry[MAX_NESTED];
};
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) {
char *p1, *p2, *p3;
char tbuf[256];
int clen;
+ int plen;
int n, i, rows;
long curr_time;
long elapsed_secs;
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';
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++;
}
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]);
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';
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)
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--;
}
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;
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;
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) {
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;
se->delta_count++;
se->total_count++;
- if (ti->depth) {
+ while (ti->depth) {
te = &ti->th_entry[ti->depth-1];
if (te->type == type) {
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;
}
}
}
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;
}
-772
+773
0xff000104 MSG_mach_notify_port_deleted
0xff000114 MSG_mach_notify_port_destroyed
0xff000118 MSG_mach_notify_no_senders
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
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
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
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
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
0x1300000 MACH_vmfault
0x1400000 MACH_SCHED
0x1400008 MACH_STKHANDOFF
+0x1400018 MACH_MKRUNNABLE
0x1500000 MACH_MSGID_INVALID
0x1090000 DecrTrap
0x1090004 DecrSet
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
PROF_LIBS = $(LIBS)
-FRAMEWORKS = -framework IOKit
+FRAMEWORKS = -framework CoreFoundation -framework IOKit
NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
{
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);
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,
*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;
(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;
char *argv[];
{
char *myname = "top";
+ int ch;
int delay = Default_DELAY;
kern_return_t error;
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;
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");
exit(1);
}
- argc--;
- argv++;
}
+
+ argc -= optind;
+ //argv += optind;
+
if (events_only)
{
if ( wide_output || do_proc0_vm)
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;
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;
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;
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);
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);
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);
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;
}