#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <stdlib.h>
#include <err.h>
#include <sys/file.h>
#include <nlist.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <paths.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_host.h>
#include "GetSymbolFromPEF.h"
#include <IOKit/graphics/IOGraphicsLib.h>
#include "IOGraphicsLibPrivate.h"
enum
{
kIOPEFparamErr = 1001,
kIOPEFmemFullErr = 1002,
kIOPEFcfragFragmentFormatErr = 1003,
kIOPEFcfragNoSectionErr = 1004,
kIOPEFcfragNoSymbolErr = 1005,
kIOPEFcfragFragmentCorruptErr = 1006
};
static unsigned char PEFGetNextByte(
unsigned char ** rawBuffer,
long * rawBufferRemaining)
{
*rawBufferRemaining = *rawBufferRemaining - 1;
return *(*rawBuffer)++;
}
static unsigned long PEFGetCount(
unsigned char ** rawBuffer,
long * rawBufferRemaining)
{
register unsigned char b;
register unsigned long value = 0UL;
b = PEFGetNextByte(rawBuffer, rawBufferRemaining);
if (!IS_LAST_PICNT_BYTE(b)) { value = CONCAT_PICNT(value, b);
b = PEFGetNextByte(rawBuffer, rawBufferRemaining);
if (!IS_LAST_PICNT_BYTE(b)) { value = CONCAT_PICNT(value, b);
b = PEFGetNextByte(rawBuffer, rawBufferRemaining);
if (!IS_LAST_PICNT_BYTE(b)) { value = CONCAT_PICNT(value, b);
b = PEFGetNextByte(rawBuffer, rawBufferRemaining);
if (!IS_LAST_PICNT_BYTE(b)) { value = CONCAT_PICNT(value, b);
b = PEFGetNextByte(rawBuffer, rawBufferRemaining);
}
}
}
}
value = CONCAT_PICNT(value, b);
return value;
}
static OSErr UnpackPiData(
LogicalAddress thePEFPtr,
SectionHeaderPtr sectionHeaderPtr,
LogicalAddress * theData)
{
long cntX, cnt, rpt, dcnt, delta;
unsigned char op, b;
unsigned char * unpackBuffer;
unsigned char * originalUnpackBuffer;
unsigned char * endUnpackBuffer;
unsigned char * oldRawBuffer;
long oldRawBufferRemaining;
unsigned char * rawBuffer;
long rawBufferRemaining;
if (sectionHeaderPtr->regionKind != kPIDataSection) {
return kIOPEFparamErr;
}
originalUnpackBuffer = (unsigned char*)NewPtrSys(sectionHeaderPtr->initSize);
if (originalUnpackBuffer == nil) {
return kIOPEFmemFullErr;
}
unpackBuffer = originalUnpackBuffer;
endUnpackBuffer = unpackBuffer + sectionHeaderPtr->initSize;
rawBuffer = (unsigned char*)((unsigned long)thePEFPtr +
sectionHeaderPtr->containerOffset);
rawBufferRemaining = sectionHeaderPtr->rawSize;
while (rawBufferRemaining > 0) {
b = PEFGetNextByte(&rawBuffer, &rawBufferRemaining);
op = PIOP(b);
cnt = PICNT(b);
if (cnt == 0) {
cnt = PEFGetCount(&rawBuffer, &rawBufferRemaining);
}
switch (op) {
case kZero: if (unpackBuffer + cnt > endUnpackBuffer) {
goto Error;
}
memset(unpackBuffer, 0, cnt);
unpackBuffer += cnt;
break;
case kBlock: if (unpackBuffer + cnt > endUnpackBuffer) {
goto Error;
}
while (--cnt >= 0) {
*unpackBuffer++ = PEFGetNextByte(&rawBuffer, &rawBufferRemaining);
}
break;
case kRepeat: rpt = PEFGetCount(&rawBuffer, &rawBufferRemaining) + 1;
if (cnt == 1) {
if (unpackBuffer + rpt > endUnpackBuffer) {
goto Error;
}
b = PEFGetNextByte(&rawBuffer, &rawBufferRemaining);
memset(unpackBuffer, b, rpt);
unpackBuffer += rpt;
} else {
oldRawBufferRemaining = rawBufferRemaining;
oldRawBuffer = rawBuffer;
while (--rpt >= 0) {
if (unpackBuffer + cnt > endUnpackBuffer) {
goto Error;
}
rawBufferRemaining = oldRawBufferRemaining;
rawBuffer = oldRawBuffer;
cntX = cnt;
while (--cntX >= 0) {
*unpackBuffer++ = PEFGetNextByte(&rawBuffer,
&rawBufferRemaining);
}
}
}
break;
case kRepeatZero: dcnt = PEFGetCount(&rawBuffer, &rawBufferRemaining); rpt = PEFGetCount(&rawBuffer, &rawBufferRemaining);
goto rptPart1;
while (--rpt >= 0) {
if (unpackBuffer + dcnt > endUnpackBuffer) {
goto Error;
}
cntX = dcnt; while (--cntX >= 0) {
*unpackBuffer++ = PEFGetNextByte(&rawBuffer,
&rawBufferRemaining);
}
rptPart1: if (unpackBuffer + cnt > endUnpackBuffer) {
goto Error;
}
memset(unpackBuffer, 0, cnt);
unpackBuffer += cnt;
}
break;
case kRepeatBlock: dcnt = PEFGetCount(&rawBuffer, &rawBufferRemaining);
rpt = PEFGetCount(&rawBuffer, &rawBufferRemaining);
oldRawBufferRemaining = rawBufferRemaining;
oldRawBuffer = rawBuffer;
delta = 0;
goto rptPart2;
while (--rpt >= 0) {
if (unpackBuffer + dcnt > endUnpackBuffer) {
goto Error;
}
rawBuffer = oldRawBuffer + cnt + delta;
rawBufferRemaining = oldRawBufferRemaining - (cnt + delta);
cntX = dcnt;
while (--cntX >= 0) {
*unpackBuffer++ = PEFGetNextByte(&rawBuffer,
&rawBufferRemaining);
}
delta += dcnt;
rptPart2:
if (unpackBuffer + cnt > endUnpackBuffer) {
goto Error;
}
rawBuffer = oldRawBuffer;
rawBufferRemaining = oldRawBufferRemaining;
cntX = cnt;
while (--cntX >= 0) {
*unpackBuffer++ = PEFGetNextByte(&rawBuffer,
&rawBufferRemaining);
}
}
rawBuffer = oldRawBuffer + cnt + delta;
rawBufferRemaining = oldRawBufferRemaining - (cnt + delta);
break;
default:
goto Error;
break;
}
}
*theData = originalUnpackBuffer;
return noErr;
Error:
if (unpackBuffer)
DisposePtr((Ptr)originalUnpackBuffer);
*theData = nil;
return kIOPEFparamErr;
}
static OSStatus GetSymbolFromPEF(
StringPtr theSymbolName,
const LogicalAddress thePEFPtr,
LogicalAddress theSymbolPtr,
ByteCount theSymbolSize)
{
ContainerHeaderPtr containerHeaderPtr; SectionHeaderPtr loaderSectionPtr = 0; SectionHeaderPtr exportSectionPtr; short currentSection;
Boolean foundSection;
Boolean foundSymbol;
long numExportSymbols;
LoaderHeaderPtr loaderHeaderPtr;
ExportSymbolEntryPtr exportSymbolEntryPtr;
LoaderExportChainEntryPtr exportChainEntryPtr;
StringPtr exportSymbolName;
LogicalAddress expandedDataPtr;
unsigned char * sourceDataPtr;
unsigned char * destDataPtr;
containerHeaderPtr = (ContainerHeaderPtr)thePEFPtr;
if (containerHeaderPtr->magicCookie != 'Joy!') {
return kIOPEFcfragFragmentFormatErr;
}
if (containerHeaderPtr->containerID != 'peff') {
return kIOPEFcfragFragmentFormatErr;
}
if (theSymbolPtr == nil) {
return kIOPEFparamErr;
}
foundSection = false;
for (currentSection = 0;
currentSection < containerHeaderPtr->nbrOfSections;
currentSection++) {
loaderSectionPtr = (SectionHeaderPtr)((unsigned long)containerHeaderPtr +
sizeof(ContainerHeader) +
(sizeof(SectionHeader) * currentSection));
if (loaderSectionPtr->regionKind == kLoaderSection) {
foundSection = true;
break;
}
}
if (foundSection == false) {
return kIOPEFcfragNoSectionErr;
}
loaderHeaderPtr = (LoaderHeaderPtr)((unsigned long)thePEFPtr +
loaderSectionPtr->containerOffset);
numExportSymbols = loaderHeaderPtr->nbrExportSyms;
exportSymbolEntryPtr = (ExportSymbolEntryPtr)((unsigned long)loaderHeaderPtr +
loaderHeaderPtr->slotTblOffset +
(sizeof(LoaderHashSlotEntry) * (1<<loaderHeaderPtr->hashSlotTblSz)) +
(sizeof(LoaderExportChainEntry) * numExportSymbols));
exportChainEntryPtr = (LoaderExportChainEntryPtr)
((unsigned long)loaderHeaderPtr +
loaderHeaderPtr->slotTblOffset +
(sizeof(LoaderHashSlotEntry) * (1<<loaderHeaderPtr->hashSlotTblSz)));
foundSymbol = false;
while (numExportSymbols-- > 0) {
exportSymbolName = (StringPtr)((unsigned long)loaderHeaderPtr +
loaderHeaderPtr->strTblOffset +
(exportSymbolEntryPtr->class_and_name & 0x00FFFFFF));
if (SymbolCompare(theSymbolName, exportSymbolName,
exportChainEntryPtr->_h._h_h._nameLength)) {
foundSymbol = true;
break;
}
exportSymbolEntryPtr = (ExportSymbolEntryPtr)
(((int)exportSymbolEntryPtr) + 10);
exportChainEntryPtr++;
}
if (foundSymbol == false) {
return kIOPEFcfragNoSymbolErr;
}
exportSectionPtr = (SectionHeaderPtr)((unsigned long)containerHeaderPtr +
sizeof(ContainerHeader) +
(sizeof(SectionHeader) * exportSymbolEntryPtr->sectionNumber));
expandedDataPtr = nil;
switch (exportSectionPtr -> regionKind) {
case kPIDataSection:
if (UnpackPiData(thePEFPtr, exportSectionPtr, &expandedDataPtr) != noErr) {
return kIOPEFcfragFragmentCorruptErr;
}
sourceDataPtr = (unsigned char*)((unsigned long)expandedDataPtr +
exportSymbolEntryPtr->address);
break;
default:
sourceDataPtr = (unsigned char*)((unsigned long)thePEFPtr +
exportSectionPtr->containerOffset +
exportSymbolEntryPtr->address);
break;
}
destDataPtr = (unsigned char*)theSymbolPtr;
while (theSymbolSize-- > 0) {
*destDataPtr++ = *sourceDataPtr++;
}
if (expandedDataPtr != nil) {
DisposePtr((Ptr)expandedDataPtr);
}
return noErr;
}
static IOByteCount GetPEFLen(LogicalAddress thePEFPtr)
{
ContainerHeaderPtr containerHeaderPtr; SectionHeaderPtr sections;
short currentSection;
long lastOffset = 0;
long len = 0;
containerHeaderPtr = (ContainerHeaderPtr)thePEFPtr;
if (containerHeaderPtr->magicCookie != 'Joy!') {
return 0;
}
if (containerHeaderPtr->containerID != 'peff') {
return 0;
}
sections = (SectionHeaderPtr) (containerHeaderPtr + 1);
for (currentSection = 0;
currentSection < containerHeaderPtr->nbrOfSections;
currentSection++) {
if (sections[currentSection].containerOffset > lastOffset) {
lastOffset = sections[currentSection].containerOffset;
len = sections[currentSection].rawSize;
}
}
return lastOffset + len;
}
static Boolean SymbolCompare(
StringPtr theLookedForSymbol,
StringPtr theExportSymbol,
unsigned long theExportSymbolLength)
{
unsigned char * p1 = (unsigned char*)theLookedForSymbol;
unsigned char * p2 = (unsigned char*)theExportSymbol;
if (theExportSymbolLength != *p1++) {
return false;
}
while (theExportSymbolLength-- != 0) {
if (*p1++ != *p2++) {
return false;
}
}
return true;
}
enum {
kInitialDriverDescriptor = 0,
kVersionOneDriverDescriptor = 1,
kTheDescriptionSignature = 'mtej',
};
struct DriverType {
unsigned char nameInfoStr[32]; unsigned long version; };
typedef struct DriverType DriverType;
struct DriverDescription {
unsigned long driverDescSignature; unsigned long driverDescVersion; DriverType driverType; };
typedef struct DriverDescription DriverDescription;
static void ExaminePEF(
mach_port_t masterPort,
char * pef,
IOByteCount pefLen,
CFDictionaryRef allMatching)
{
char descripName[] = "\pTheDriverDescription";
long err;
DriverDescription descrip;
char matchName[40];
unsigned long newVersion;
unsigned long curVersion;
IOReturn kr;
io_iterator_t iter;
io_service_t service;
io_string_t path;
CFStringRef ndrvPropName = CFSTR("driver,AAPL,MacOS,PowerPC");
CFDataRef ndrv;
CFStringRef matchKey;
CFTypeRef value = 0;
CFDictionaryRef matching = 0;
CFMutableDictionaryRef dict;
err = GetSymbolFromPEF(descripName, pef, &descrip, sizeof(descrip));
if (err != 0) {
printf("\nGetSymbolFromPEF returns %ld\n",err);
return;
}
if ((descrip.driverDescSignature != kTheDescriptionSignature) ||
(descrip.driverDescVersion != kInitialDriverDescriptor)) {
return;
}
strncpy(matchName, descrip.driverType.nameInfoStr + 1,
descrip.driverType.nameInfoStr[0]);
matchName[descrip.driverType.nameInfoStr[0]] = 0;
matchKey = CFStringCreateWithCString( kCFAllocatorDefault, matchName,
kCFStringEncodingMacRoman );
if (!matchKey)
return;
if (allMatching)
{
value = CFDictionaryGetValue(allMatching, matchKey);
if (value)
{
if (value && (CFDictionaryGetTypeID() == CFGetTypeID(value)))
matching = CFRetain(value);
else if (value && (CFStringGetTypeID() == CFGetTypeID(value)))
{
CFRelease(matchKey);
matchKey = CFRetain(value);
}
}
}
if (!matching)
{
CFStringRef nameMatchKey = CFSTR(kIONameMatchKey);
matching = CFDictionaryCreate( kCFAllocatorDefault,
(const void **) &nameMatchKey, (const void **) &matchKey, 1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
}
CFRelease(matchKey);
if (!matching)
return;
kr = IOServiceGetMatchingServices(masterPort, matching, &iter);
if (kIOReturnSuccess != kr)
return;
newVersion = descrip.driverType.version;
if ((newVersion & 0xffff) == 0x8000) {
newVersion |= 0xff;
}
for ( ; (service = IOIteratorNext(iter)); IOObjectRelease(service))
{
kr = IORegistryEntryGetPath(service, kIOServicePlane, path);
if (kIOReturnSuccess == kr)
printf("Name %s matches %s, ", matchName, path);
ndrv = (CFDataRef) IORegistryEntryCreateCFProperty(service, ndrvPropName,
kCFAllocatorDefault, kNilOptions);
if (ndrv)
{
DriverDescription _curDesc;
DriverDescription * curDesc;
curDesc = (DriverDescription *) CFDataGetBytePtr(ndrv);
err = noErr;
if ((sizeof(DriverDescription) > CFDataGetLength(ndrv))
|| (curDesc->driverDescSignature != kTheDescriptionSignature))
{
curDesc = &_curDesc;
err = GetSymbolFromPEF(descripName,
(const LogicalAddress)CFDataGetBytePtr(ndrv),
curDesc, sizeof(DriverDescription));
}
if (err != noErr)
printf("GetSymbolFromPEF returns %ld\n",err);
else
{
if ((curDesc->driverDescSignature == kTheDescriptionSignature) &&
(curDesc->driverDescVersion == kInitialDriverDescriptor))
{
curVersion = curDesc->driverType.version;
printf("new version %08lx, current version %08lx\n",
newVersion, curVersion);
if ((curVersion & 0xffff) == 0x8000) {
curVersion |= 0xff;
}
if (newVersion <= curVersion) {
pefLen = 0;
}
}
}
CFRelease(ndrv);
}
if (pefLen == 0)
continue;
ndrv = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
pef, pefLen, kCFAllocatorNull);
if (ndrv == 0)
continue;
printf("Installing ndrv (");
dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (dict)
{
io_service_t child = MACH_PORT_NULL;
io_iterator_t iter;
CFDictionarySetValue(dict, ndrvPropName, ndrv);
kr = IORegistryEntryGetChildIterator(service, kIOServicePlane, &iter);
if (kr == kIOReturnSuccess)
{
kr = kIOReturnNotFound;
for( ;
(child = IOIteratorNext(iter));
IOObjectRelease(child)) {
if (IOObjectConformsTo(child, "IOFramebuffer"))
break;
}
IOObjectRelease(iter);
}
if (child)
{
kr = IORegistryEntrySetCFProperties(child, dict);
IOObjectRelease(child);
}
CFRelease(dict);
}
else
kr = kIOReturnNoMemory;
CFRelease(ndrv);
printf("%08x)\n", kr);
}
IOObjectRelease(iter);
return;
}
static int PEFExamineFile(mach_port_t masterPort, CFURLRef file, CFDictionaryRef props)
{
vm_offset_t pefBytes;
vm_size_t pefFileLen;
char * pef;
IOByteCount pefLen, pos = 0;
int err;
CFDictionaryRef fileMatch;
CFDictionaryRef matching = 0;
Boolean matches = true;
char cFile[MAXPATHLEN];
do
{
if (!props)
continue;
fileMatch = CFDictionaryGetValue(props, CFSTR("IONDRVFileMatching"));
if (fileMatch && (CFDictionaryGetTypeID() != CFGetTypeID(fileMatch)))
fileMatch = 0;
if (fileMatch)
{
io_service_t service;
CFRetain(fileMatch);
service = IOServiceGetMatchingService(masterPort, fileMatch);
matches = (MACH_PORT_NULL != service);
if (matches)
IOObjectRelease(service);
continue;
}
matching = CFDictionaryGetValue(props, CFSTR("IONDRVMatching"));
if (matching && (CFDictionaryGetTypeID() != CFGetTypeID(matching)))
matching = 0;
}
while (false);
if (!matches)
return (kIOReturnSuccess);
if (CFURLGetFileSystemRepresentation(file, TRUE, cFile, MAXPATHLEN))
err = readFile(cFile, &pefBytes, &pefFileLen);
else
err = kIOReturnIOError;
if (kIOReturnSuccess != err)
return (err);
pef = (char *) pefBytes;
while ((pos < pefFileLen) && (pefLen = GetPEFLen(pef)))
{
ExaminePEF(masterPort, pef, pefLen, matching);
pefLen = (pefLen + 15) & ~15;
pef += pefLen;
pos += pefLen;
}
if (pefBytes)
vm_deallocate(mach_task_self(), pefBytes, pefFileLen);
return (0);
}
static char * CFURLCopyCString(CFURLRef anURL)
{
char * string = NULL; CFIndex bufferLength;
CFStringRef urlString = NULL; Boolean error = false;
urlString = CFURLCopyFileSystemPath(anURL, kCFURLPOSIXPathStyle);
if (!urlString) {
goto finish;
}
bufferLength = 1 + CFStringGetLength(urlString);
string = (char *)malloc(bufferLength * sizeof(char));
if (!string) {
goto finish;
}
if (!CFStringGetCString(urlString, string, bufferLength,
kCFStringEncodingMacRoman)) {
error = true;
goto finish;
}
finish:
if (error) {
free(string);
string = NULL;
}
if (urlString) CFRelease(urlString);
return string;
}
static void _PEFExamineFile(mach_port_t masterPort, CFURLRef ndrvURL, CFDictionaryRef plist)
{
if (PEFExamineFile(masterPort, ndrvURL, plist))
{
char * ndrv_path = CFURLCopyCString(ndrvURL);
printf("error processing NDRV \"%s\"",
ndrv_path ? ndrv_path : "(unknown)");
if (ndrv_path)
free(ndrv_path);
}
}
static void PEFExamineBundle( mach_port_t masterPort, CFBundleRef bdl )
{
CFURLRef ndrvURL;
CFDictionaryRef plist;
plist = CFBundleGetInfoDictionary(bdl);
if (!plist)
return;
ndrvURL = CFBundleCopyExecutableURL(bdl);
if (!ndrvURL)
return;
_PEFExamineFile(kIOMasterPortDefault, ndrvURL, plist);
CFRelease(ndrvURL);
}
void IOLoadPEFsFromURL( CFURLRef ndrvDirURL, io_service_t service )
{
CFIndex ndrvCount, n;
CFArrayRef ndrvDirContents = NULL; SInt32 error;
ndrvDirContents = (CFArrayRef) CFURLCreatePropertyFromResource(
kCFAllocatorDefault, ndrvDirURL, kCFURLFileDirectoryContents,
&error);
ndrvCount = ndrvDirContents ? CFArrayGetCount(ndrvDirContents) : 0;
for (n = 0; n < ndrvCount; n++)
{
CFURLRef ndrvURL = NULL; CFNumberRef num;
CFBundleRef bdl;
CFStringRef ext;
SInt32 mode;
Boolean skip;
ndrvURL = (CFURLRef)CFArrayGetValueAtIndex(ndrvDirContents, n);
bdl = CFBundleCreate(kCFAllocatorDefault, ndrvURL);
if (bdl)
{
PEFExamineBundle(kIOMasterPortDefault, bdl);
CFRelease(bdl);
continue;
}
ext = CFURLCopyPathExtension(ndrvURL);
if (ext)
{
skip = CFEqual(ext, CFSTR(".plist"));
CFRelease(ext);
if (skip)
continue;
}
num = (CFNumberRef) CFURLCreatePropertyFromResource(
kCFAllocatorDefault, ndrvURL, kCFURLFilePOSIXMode, &error);
if (!num)
continue;
CFNumberGetValue(num, kCFNumberSInt32Type, (SInt32 *) &mode);
CFRelease(num);
if ((mode & S_IFMT) == S_IFREG)
_PEFExamineFile(kIOMasterPortDefault, ndrvURL, NULL);
}
if (ndrvDirContents)
CFRelease(ndrvDirContents);
}