mirror of https://github.com/python/cpython
1361 lines
48 KiB
C
1361 lines
48 KiB
C
|
/*
|
|||
|
File: CFMLateImport.c
|
|||
|
|
|||
|
Contains: Implementation of CFM late import library.
|
|||
|
|
|||
|
Written by: Quinn
|
|||
|
|
|||
|
Copyright: Copyright <EFBFBD> 1999 by Apple Computer, Inc., all rights reserved.
|
|||
|
|
|||
|
You may incorporate this Apple sample source code into your program(s) without
|
|||
|
restriction. This Apple sample source code has been provided "AS IS" and the
|
|||
|
responsibility for its operation is yours. You are not permitted to redistribute
|
|||
|
this Apple sample source code as "Apple sample source code" after having made
|
|||
|
changes. If you're going to re-distribute the source, we require that you make
|
|||
|
it clear in the source that the code was descended from Apple sample source
|
|||
|
code, but that you've made changes.
|
|||
|
|
|||
|
Change History (most recent first):
|
|||
|
|
|||
|
<13> 24/9/01 Quinn Fixes to compile with C++ activated.
|
|||
|
<12> 21/9/01 Quinn [2710489] Fix typo in the comments for FragmentLookup.
|
|||
|
<11> 21/9/01 Quinn Changes for CWPro7 Mach-O build.
|
|||
|
<10> 19/9/01 Quinn Corrected implementation of kPEFRelocSmBySection. Added
|
|||
|
implementations of kPEFRelocSetPosition and kPEFRelocLgByImport
|
|||
|
(from code contributed by Eric Grant, Ned Holbrook, and Steve
|
|||
|
Kalkwarf), although I can't test them yet.
|
|||
|
<9> 19/9/01 Quinn We now handle unpacked data sections, courtesy of some code from
|
|||
|
Ned Holbrook.
|
|||
|
<8> 19/9/01 Quinn Minor fixes for the previous checkin. Updated some comments and
|
|||
|
killed some dead code.
|
|||
|
<7> 19/9/01 Quinn Simplified API and implementation after a suggestion by Eric
|
|||
|
Grant. You no longer have to CFM export a dummy function; you
|
|||
|
can just pass in the address of your fragment's init routine.
|
|||
|
<6> 15/2/01 Quinn Modify compile-time warnings to complain if you try to build
|
|||
|
this module into a Mach-O binary.
|
|||
|
<5> 5/2/01 Quinn Removed redundant assignment in CFMLateImportCore.
|
|||
|
<4> 30/11/00 Quinn Added comment about future of data symbols in CF.
|
|||
|
<3> 16/11/00 Quinn Allow symbol finding via a callback and use that to implement
|
|||
|
CFBundle support.
|
|||
|
<2> 18/10/99 Quinn Renamed CFMLateImport to CFMLateImportLibrary to allow for
|
|||
|
possible future API expansion.
|
|||
|
<1> 15/6/99 Quinn First checked in.
|
|||
|
*/
|
|||
|
|
|||
|
// To Do List:
|
|||
|
//
|
|||
|
// o get rid of dependence on ANSI "string.h", but how?
|
|||
|
//
|
|||
|
// Done:
|
|||
|
//
|
|||
|
// <20> investigate alternative APIs, like an external lookup routine
|
|||
|
// renamed CFMLateImport to CFMLateImportLibrary to allow for
|
|||
|
// future expansion of the APIs for things like CFMLateImportSymbol
|
|||
|
// <20> test with non-zero fragment offset in the file
|
|||
|
// <20> test more with MPW fragments
|
|||
|
// <20> test data imports
|
|||
|
|
|||
|
/////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
// MoreIsBetter Setup
|
|||
|
|
|||
|
//#include "MoreSetup.h"
|
|||
|
#define MoreAssert(x) (true)
|
|||
|
#define MoreAssertQ(x)
|
|||
|
|
|||
|
// Mac OS Interfaces
|
|||
|
|
|||
|
#if ! MORE_FRAMEWORK_INCLUDES
|
|||
|
#include <CodeFragments.h>
|
|||
|
#include <PEFBinaryFormat.h>
|
|||
|
#endif
|
|||
|
|
|||
|
// Standard C Interfaces
|
|||
|
|
|||
|
#include <string.h>
|
|||
|
|
|||
|
// MIB Prototypes
|
|||
|
|
|||
|
//#include "MoreInterfaceLib.h"
|
|||
|
#define MoreBlockZero BlockZero
|
|||
|
|
|||
|
// Our Prototypes
|
|||
|
|
|||
|
#include "CFMLateImport.h"
|
|||
|
|
|||
|
/////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
#if TARGET_RT_MAC_MACHO
|
|||
|
#error CFMLateImport is not suitable for use in a Mach-O project.
|
|||
|
#elif !TARGET_RT_MAC_CFM || !TARGET_CPU_PPC
|
|||
|
#error CFMLateImport has not been qualified for 68K or CFM-68K use.
|
|||
|
#endif
|
|||
|
|
|||
|
/////////////////////////////////////////////////////////////////
|
|||
|
#pragma mark ----- Utility Routines -----
|
|||
|
|
|||
|
static OSStatus FSReadAtOffset(SInt16 refNum, SInt32 offset, SInt32 count, void *buffer)
|
|||
|
// A convenient wrapper around PBRead which has two advantages
|
|||
|
// over FSRead. First, it takes count as a value parameter.
|
|||
|
// Second, it reads from an arbitrary offset into the file,
|
|||
|
// which avoids a bunch of SetFPos calls.
|
|||
|
//
|
|||
|
// I guess this should go into "MoreFiles.h", but I'm not sure
|
|||
|
// how we're going to integrate such a concept into MIB yet.
|
|||
|
{
|
|||
|
ParamBlockRec pb;
|
|||
|
|
|||
|
pb.ioParam.ioRefNum = refNum;
|
|||
|
pb.ioParam.ioBuffer = (Ptr) buffer;
|
|||
|
pb.ioParam.ioReqCount = count;
|
|||
|
pb.ioParam.ioPosMode = fsFromStart;
|
|||
|
pb.ioParam.ioPosOffset = offset;
|
|||
|
|
|||
|
return PBReadSync(&pb);
|
|||
|
}
|
|||
|
|
|||
|
/////////////////////////////////////////////////////////////////
|
|||
|
#pragma mark ----- Late Import Engine -----
|
|||
|
|
|||
|
// This structure represents the core data structure of the late import
|
|||
|
// engine. It basically holds information about the fragment we're going
|
|||
|
// to fix up. It starts off with the first three fields, which are
|
|||
|
// provided by the client. Then, as we procede through the operation,
|
|||
|
// we fill out more fields.
|
|||
|
|
|||
|
struct FragToFixInfo {
|
|||
|
CFragSystem7DiskFlatLocator locator; // How to find the fragment's container.
|
|||
|
CFragConnectionID connID; // CFM connection to the fragment.
|
|||
|
CFragInitFunction initRoutine; // The CFM init routine for the fragment.
|
|||
|
PEFContainerHeader containerHeader; // The CFM header, read in from the container.
|
|||
|
PEFSectionHeader *sectionHeaders; // The CFM section headers. A pointer block containing an array of containerHeader.sectionCount elements.
|
|||
|
PEFLoaderInfoHeader *loaderSection; // The entire CFM loader section in a pointer block.
|
|||
|
SInt16 fileRef; // A read-only path to the CFM container. We keep this here because one that one routine needs to read from the container.
|
|||
|
void *section0Base; // The base address of section 0, which we go through hoops to calculate.
|
|||
|
void *section1Base; // The base address of section 1, which we go through hoops to calculate.
|
|||
|
Boolean disposeSectionPointers; // See below.
|
|||
|
};
|
|||
|
typedef struct FragToFixInfo FragToFixInfo;
|
|||
|
|
|||
|
// The disposeSectionPointers Boolean is designed for future cool VM
|
|||
|
// support. If VM is on, the entire code fragment is file mapped into
|
|||
|
// high memory, including the data we're forced to allocate the
|
|||
|
// sectionHeaders and loaderSection memory blocks to maintain. If
|
|||
|
// we could find the address of the entire file mapped container,
|
|||
|
// we could access the information directly from there and thus
|
|||
|
// we wouldn't need to allocate (or dispose of) the memory blocks
|
|||
|
// for sectionHeaders and loaderSection.
|
|||
|
//
|
|||
|
// I haven't implemented this yet because a) I'm not sure how to do
|
|||
|
// it with documented APIs, and b) I couldn't be bothered, but
|
|||
|
// disposeSectionPointers remains as vestigial support for the concept.
|
|||
|
|
|||
|
static OSStatus ReadContainerBasics(FragToFixInfo *fragToFix)
|
|||
|
// Reads some basic information from the container of the
|
|||
|
// fragment to fix and stores it in various fields of
|
|||
|
// fragToFix. This includes:
|
|||
|
//
|
|||
|
// o containerHeader -- The contain header itself.
|
|||
|
// o sectionHeaders -- The array of section headers (in a newly allocated pointer block).
|
|||
|
// o loaderSection -- The entire loader section (in a newly allocated pointer block).
|
|||
|
//
|
|||
|
// Also sets disposeSectionPointers to indicate whether
|
|||
|
// the last two pointers should be disposed of.
|
|||
|
//
|
|||
|
// Finally, it leaves the container file open for later
|
|||
|
// folks who want to read data from it.
|
|||
|
{
|
|||
|
OSStatus err;
|
|||
|
UInt16 sectionIndex;
|
|||
|
Boolean found;
|
|||
|
|
|||
|
MoreAssertQ(fragToFix != nil);
|
|||
|
MoreAssertQ(fragToFix->locator.fileSpec != nil);
|
|||
|
MoreAssertQ(fragToFix->connID != nil);
|
|||
|
MoreAssertQ(fragToFix->loaderSection == nil);
|
|||
|
MoreAssertQ(fragToFix->sectionHeaders == nil);
|
|||
|
MoreAssertQ(fragToFix->fileRef == 0);
|
|||
|
|
|||
|
fragToFix->disposeSectionPointers = true;
|
|||
|
|
|||
|
// Open up the file, read the container head, then read in
|
|||
|
// all the section headers, then go looking through the
|
|||
|
// section headers for the loader section (PEF defines
|
|||
|
// that there can be only one).
|
|||
|
|
|||
|
err = FSpOpenDF(fragToFix->locator.fileSpec, fsRdPerm, &fragToFix->fileRef);
|
|||
|
if (err == noErr) {
|
|||
|
err = FSReadAtOffset(fragToFix->fileRef,
|
|||
|
fragToFix->locator.offset,
|
|||
|
sizeof(fragToFix->containerHeader),
|
|||
|
&fragToFix->containerHeader);
|
|||
|
if (err == noErr) {
|
|||
|
if ( fragToFix->containerHeader.tag1 != kPEFTag1
|
|||
|
|| fragToFix->containerHeader.tag2 != kPEFTag2
|
|||
|
|| fragToFix->containerHeader.architecture != kCompiledCFragArch
|
|||
|
|| fragToFix->containerHeader.formatVersion != kPEFVersion) {
|
|||
|
err = cfragFragmentFormatErr;
|
|||
|
}
|
|||
|
}
|
|||
|
if (err == noErr) {
|
|||
|
fragToFix->sectionHeaders = (PEFSectionHeader *) NewPtr(fragToFix->containerHeader.sectionCount * sizeof(PEFSectionHeader));
|
|||
|
err = MemError();
|
|||
|
}
|
|||
|
if (err == noErr) {
|
|||
|
err = FSReadAtOffset(fragToFix->fileRef,
|
|||
|
fragToFix->locator.offset + sizeof(fragToFix->containerHeader),
|
|||
|
fragToFix->containerHeader.sectionCount * sizeof(PEFSectionHeader),
|
|||
|
fragToFix->sectionHeaders);
|
|||
|
}
|
|||
|
if (err == noErr) {
|
|||
|
sectionIndex = 0;
|
|||
|
found = false;
|
|||
|
while ( sectionIndex < fragToFix->containerHeader.sectionCount && ! found ) {
|
|||
|
found = (fragToFix->sectionHeaders[sectionIndex].sectionKind == kPEFLoaderSection);
|
|||
|
if ( ! found ) {
|
|||
|
sectionIndex += 1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (err == noErr && ! found) {
|
|||
|
err = cfragNoSectionErr;
|
|||
|
}
|
|||
|
|
|||
|
// Now read allocate a pointer block and read the loader section into it.
|
|||
|
|
|||
|
if (err == noErr) {
|
|||
|
fragToFix->loaderSection = (PEFLoaderInfoHeader *) NewPtr(fragToFix->sectionHeaders[sectionIndex].containerLength);
|
|||
|
err = MemError();
|
|||
|
}
|
|||
|
if (err == noErr) {
|
|||
|
err = FSReadAtOffset(fragToFix->fileRef,
|
|||
|
fragToFix->locator.offset + fragToFix->sectionHeaders[sectionIndex].containerOffset,
|
|||
|
fragToFix->sectionHeaders[sectionIndex].containerLength,
|
|||
|
fragToFix->loaderSection);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// No clean up. The client must init fragToFix to zeros and then
|
|||
|
// clean up regardless of whether we return an error.
|
|||
|
|
|||
|
return err;
|
|||
|
}
|
|||
|
|
|||
|
static UInt32 DecodeVCountValue(const UInt8 *start, UInt32 *outCount)
|
|||
|
// Given a pointer to the start of a variable length PEF value,
|
|||
|
// work out the value (in *outCount). Returns the number of bytes
|
|||
|
// consumed by the value.
|
|||
|
{
|
|||
|
UInt8 * bytePtr;
|
|||
|
UInt8 byte;
|
|||
|
UInt32 count;
|
|||
|
|
|||
|
bytePtr = (UInt8 *)start;
|
|||
|
|
|||
|
// Code taken from "PEFBinaryFormat.h".
|
|||
|
count = 0;
|
|||
|
do {
|
|||
|
byte = *bytePtr++;
|
|||
|
count = (count << kPEFPkDataVCountShift) | (byte & kPEFPkDataVCountMask);
|
|||
|
} while ((byte & kPEFPkDataVCountEndMask) != 0);
|
|||
|
|
|||
|
*outCount = count;
|
|||
|
return bytePtr - start;
|
|||
|
}
|
|||
|
|
|||
|
static UInt32 DecodeInstrCountValue(const UInt8 *inOpStart, UInt32 *outCount)
|
|||
|
// Given a pointer to the start of an opcode (inOpStart), work out the
|
|||
|
// count argument for that opcode (*outCount). Returns the number of
|
|||
|
// bytes consumed by the opcode and count combination.
|
|||
|
{
|
|||
|
MoreAssertQ(inOpStart != nil);
|
|||
|
MoreAssertQ(outCount != nil);
|
|||
|
|
|||
|
if (PEFPkDataCount5(*inOpStart) != 0)
|
|||
|
{
|
|||
|
// Simple case, count encoded in opcode.
|
|||
|
*outCount = PEFPkDataCount5(*inOpStart);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Variable-length case.
|
|||
|
return 1 + DecodeVCountValue(inOpStart + 1, outCount);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static OSStatus UnpackPEFDataSection(const UInt8 * const packedData, UInt32 packedSize,
|
|||
|
UInt8 * const unpackedData, UInt32 unpackedSize)
|
|||
|
{
|
|||
|
OSErr err;
|
|||
|
UInt32 offset;
|
|||
|
UInt8 opCode;
|
|||
|
UInt8 * unpackCursor;
|
|||
|
|
|||
|
MoreAssertQ(packedData != nil);
|
|||
|
MoreAssertQ(unpackedData != nil);
|
|||
|
MoreAssertQ(unpackedSize >= packedSize);
|
|||
|
|
|||
|
// The following asserts assume that the client allocated the memory with NewPtr,
|
|||
|
// which may not always be true. However, the asserts' value in preventing accidental
|
|||
|
// memory block overruns outweighs the possible maintenance effort.
|
|||
|
|
|||
|
MoreAssertQ( packedSize == GetPtrSize( (Ptr) packedData ) );
|
|||
|
MoreAssertQ( unpackedSize == GetPtrSize( (Ptr) unpackedData) );
|
|||
|
|
|||
|
err = noErr;
|
|||
|
offset = 0;
|
|||
|
unpackCursor = unpackedData;
|
|||
|
while (offset < packedSize) {
|
|||
|
MoreAssertQ(unpackCursor < &unpackedData[unpackedSize]);
|
|||
|
|
|||
|
opCode = packedData[offset];
|
|||
|
|
|||
|
switch (PEFPkDataOpcode(opCode)) {
|
|||
|
case kPEFPkDataZero:
|
|||
|
{
|
|||
|
UInt32 count;
|
|||
|
|
|||
|
offset += DecodeInstrCountValue(&packedData[offset], &count);
|
|||
|
|
|||
|
MoreBlockZero(unpackCursor, count);
|
|||
|
unpackCursor += count;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case kPEFPkDataBlock:
|
|||
|
{
|
|||
|
UInt32 blockSize;
|
|||
|
|
|||
|
offset += DecodeInstrCountValue(&packedData[offset], &blockSize);
|
|||
|
|
|||
|
BlockMoveData(&packedData[offset], unpackCursor, blockSize);
|
|||
|
unpackCursor += blockSize;
|
|||
|
offset += blockSize;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case kPEFPkDataRepeat:
|
|||
|
{
|
|||
|
UInt32 blockSize;
|
|||
|
UInt32 repeatCount;
|
|||
|
UInt32 loopCounter;
|
|||
|
|
|||
|
offset += DecodeInstrCountValue(&packedData[offset], &blockSize);
|
|||
|
offset += DecodeVCountValue(&packedData[offset], &repeatCount);
|
|||
|
repeatCount += 1; // stored value is (repeatCount - 1)
|
|||
|
|
|||
|
for (loopCounter = 0; loopCounter < repeatCount; loopCounter++) {
|
|||
|
BlockMoveData(&packedData[offset], unpackCursor, blockSize);
|
|||
|
unpackCursor += blockSize;
|
|||
|
}
|
|||
|
offset += blockSize;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case kPEFPkDataRepeatBlock:
|
|||
|
{
|
|||
|
UInt32 commonSize;
|
|||
|
UInt32 customSize;
|
|||
|
UInt32 repeatCount;
|
|||
|
const UInt8 *commonData;
|
|||
|
const UInt8 *customData;
|
|||
|
UInt32 loopCounter;
|
|||
|
|
|||
|
offset += DecodeInstrCountValue(&packedData[offset], &commonSize);
|
|||
|
offset += DecodeVCountValue(&packedData[offset], &customSize);
|
|||
|
offset += DecodeVCountValue(&packedData[offset], &repeatCount);
|
|||
|
|
|||
|
commonData = &packedData[offset];
|
|||
|
customData = &packedData[offset + commonSize];
|
|||
|
|
|||
|
for (loopCounter = 0; loopCounter < repeatCount; loopCounter++) {
|
|||
|
BlockMoveData(commonData, unpackCursor, commonSize);
|
|||
|
unpackCursor += commonSize;
|
|||
|
BlockMoveData(customData, unpackCursor, customSize);
|
|||
|
unpackCursor += customSize;
|
|||
|
customData += customSize;
|
|||
|
}
|
|||
|
BlockMoveData(commonData, unpackCursor, commonSize);
|
|||
|
unpackCursor += commonSize;
|
|||
|
offset += (repeatCount * (commonSize + customSize)) + commonSize;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case kPEFPkDataRepeatZero:
|
|||
|
{
|
|||
|
UInt32 commonSize;
|
|||
|
UInt32 customSize;
|
|||
|
UInt32 repeatCount;
|
|||
|
const UInt8 *customData;
|
|||
|
UInt32 loopCounter;
|
|||
|
|
|||
|
offset += DecodeInstrCountValue(&packedData[offset], &commonSize);
|
|||
|
offset += DecodeVCountValue(&packedData[offset], &customSize);
|
|||
|
offset += DecodeVCountValue(&packedData[offset], &repeatCount);
|
|||
|
|
|||
|
customData = &packedData[offset];
|
|||
|
|
|||
|
for (loopCounter = 0; loopCounter < repeatCount; loopCounter++) {
|
|||
|
MoreBlockZero(unpackCursor, commonSize);
|
|||
|
unpackCursor += commonSize;
|
|||
|
BlockMoveData(customData, unpackCursor, customSize);
|
|||
|
unpackCursor += customSize;
|
|||
|
customData += customSize;
|
|||
|
}
|
|||
|
MoreBlockZero(unpackCursor, commonSize);
|
|||
|
unpackCursor += commonSize;
|
|||
|
offset += repeatCount * customSize;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
#if MORE_DEBUG
|
|||
|
DebugStr("\pUnpackPEFDataSection: Unexpected data opcode");
|
|||
|
#endif
|
|||
|
err = cfragFragmentCorruptErr;
|
|||
|
goto leaveNow;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
leaveNow:
|
|||
|
return err;
|
|||
|
}
|
|||
|
|
|||
|
/* SetupSectionBaseAddresses Rationale
|
|||
|
-----------------------------------
|
|||
|
|
|||
|
OK, here's where things get weird. In order to run the relocation
|
|||
|
engine, I need to be able to find the base address of an instantiated
|
|||
|
section of the fragment we're fixing up given only its section number.
|
|||
|
This isn't hard for CFM to do because it's the one that instantiated the
|
|||
|
sections in the first place. It's surprisingly difficult to do if
|
|||
|
you're not CFM. [And you don't have access to the private CFM APis for
|
|||
|
doing it.]
|
|||
|
|
|||
|
[Alan Lillich is going to kill me when he reads this! I should point out
|
|||
|
that TVector's don't have to contain two words, they can be longer,
|
|||
|
and that the second word isn't necessarily a TOC pointer, it's
|
|||
|
just that the calling conventions require that it be put in the
|
|||
|
TOC register when the code is called.
|
|||
|
|
|||
|
Furthermore, the code section isn't always section 0, and the data
|
|||
|
section isn't always section 1, and there can be zero to many sections
|
|||
|
of each type.
|
|||
|
|
|||
|
But these niceties are besides the point: I'm doing something tricky
|
|||
|
because I don't have a nice API for getting section base addresses.
|
|||
|
If I had a nice API for doing that, none of this code would exist.
|
|||
|
]
|
|||
|
|
|||
|
The technique is very sneaky (thanks to Eric Grant). The fragment to
|
|||
|
fix necessarily has a CFM init routine (because it needs that routine
|
|||
|
in order to capture the fragment location and connection ID). Thus the
|
|||
|
fragment to fix must have a TVector in its data section. TVectors are
|
|||
|
interesting because they're made up of two words. The first is a pointer
|
|||
|
to the code that implements the routine; the second is a pointer to the TOC
|
|||
|
for the fragment that's exporting the TVector. How TVectors are
|
|||
|
created is interesting too. On disk, a TVector consists of two words,
|
|||
|
the first being the offset from the start of the code section to the
|
|||
|
routine, the second being the offset from the start of the data section
|
|||
|
to the TOC base. When CFM prepares a TVector, it applies the following
|
|||
|
transform:
|
|||
|
|
|||
|
tvector.codePtr = tvector.codeOffset + base of code section
|
|||
|
tvector.tocPtr = tvector.tocOffset + base of data section
|
|||
|
|
|||
|
Now, you can reverse these questions to make them:
|
|||
|
|
|||
|
base of code section = tvector.codePtr - tvector.codeOffset
|
|||
|
base of data section = tvector.dataPtr - tvector.dataOffset
|
|||
|
|
|||
|
So if you can find the relocated contents of the TVector and
|
|||
|
find the original offsets that made up the TVector, you can then
|
|||
|
calculate the base address of both the code and data sections.
|
|||
|
|
|||
|
Finding the relocated contents of the TVector is easy; I simply
|
|||
|
require the client to pass in a pointer to its init routine.
|
|||
|
A routine pointer is a TVector pointer, so you can just cast it
|
|||
|
and extract the pair of words.
|
|||
|
|
|||
|
Finding the original offsets is a trickier. My technique is to
|
|||
|
look up the init routine in the fragment's loader info header. This
|
|||
|
yields the section number and offset where the init routine's unrelocated
|
|||
|
TVector exists. Once I have that, I can just read the unrelocated TVector
|
|||
|
out of the file and extract the offsets.
|
|||
|
*/
|
|||
|
|
|||
|
struct TVector {
|
|||
|
void *codePtr;
|
|||
|
void *tocPtr;
|
|||
|
};
|
|||
|
typedef struct TVector TVector;
|
|||
|
|
|||
|
static OSStatus SetupSectionBaseAddresses(FragToFixInfo *fragToFix)
|
|||
|
// This routine initialises the section0Base and section1Base
|
|||
|
// base fields of fragToFix to the base addresses of the
|
|||
|
// instantiated fragment represented by the other fields
|
|||
|
// of fragToFix. The process works in three states:
|
|||
|
//
|
|||
|
// 1. Find the contents of the relocated TVector of the
|
|||
|
// fragment's initialisation routine, provided to us by
|
|||
|
// the caller.
|
|||
|
//
|
|||
|
// 2. Find the contents of the non-relocated TVector by
|
|||
|
// looking it up in the PEF loader info header and then
|
|||
|
// using that to read the TVector contents from disk.
|
|||
|
// This yields the offsets from the section bases for
|
|||
|
// the init routine.
|
|||
|
//
|
|||
|
// 3. Subtract 2 from 3.
|
|||
|
{
|
|||
|
OSStatus err;
|
|||
|
TVector * relocatedExport;
|
|||
|
SInt32 initSection;
|
|||
|
UInt32 initOffset;
|
|||
|
PEFSectionHeader * initSectionHeader;
|
|||
|
Ptr packedDataSection;
|
|||
|
Ptr unpackedDataSection;
|
|||
|
TVector originalOffsets;
|
|||
|
|
|||
|
packedDataSection = nil;
|
|||
|
unpackedDataSection = nil;
|
|||
|
|
|||
|
// Step 1.
|
|||
|
|
|||
|
// First find the init routine's TVector, which gives us the relocated
|
|||
|
// offsets of the init routine into the data and code sections.
|
|||
|
|
|||
|
relocatedExport = (TVector *) fragToFix->initRoutine;
|
|||
|
|
|||
|
// Step 2.
|
|||
|
|
|||
|
// Now find the init routine's TVector's offsets in the data section on
|
|||
|
// disk. This gives us the raw offsets from the data and code section
|
|||
|
// of the beginning of the init routine.
|
|||
|
|
|||
|
err = noErr;
|
|||
|
initSection = fragToFix->loaderSection->initSection;
|
|||
|
initOffset = fragToFix->loaderSection->initOffset;
|
|||
|
if (initSection == -1) {
|
|||
|
err = cfragFragmentUsageErr;
|
|||
|
}
|
|||
|
if (err == noErr) {
|
|||
|
MoreAssertQ( initSection >= 0 ); // Negative indexes are pseudo-sections which are just not allowed!
|
|||
|
MoreAssertQ( initSection < fragToFix->containerHeader.sectionCount );
|
|||
|
|
|||
|
initSectionHeader = &fragToFix->sectionHeaders[initSection];
|
|||
|
|
|||
|
// If the data section is packed, unpack it to a temporary buffer and then get the
|
|||
|
// original offsets from that buffer. If the data section is unpacked, just read
|
|||
|
// the original offsets directly off the disk.
|
|||
|
|
|||
|
if ( initSectionHeader->sectionKind == kPEFPackedDataSection ) {
|
|||
|
|
|||
|
// Allocate space for packed and unpacked copies of the section.
|
|||
|
|
|||
|
packedDataSection = NewPtr(initSectionHeader->containerLength);
|
|||
|
err = MemError();
|
|||
|
|
|||
|
if (err == noErr) {
|
|||
|
unpackedDataSection = NewPtr(initSectionHeader->unpackedLength);
|
|||
|
err = MemError();
|
|||
|
}
|
|||
|
|
|||
|
// Read the contents of the packed section.
|
|||
|
|
|||
|
if (err == noErr) {
|
|||
|
err = FSReadAtOffset( fragToFix->fileRef,
|
|||
|
fragToFix->locator.offset
|
|||
|
+ initSectionHeader->containerOffset,
|
|||
|
initSectionHeader->containerLength,
|
|||
|
packedDataSection);
|
|||
|
}
|
|||
|
|
|||
|
// Unpack the data into the unpacked section.
|
|||
|
|
|||
|
if (err == noErr) {
|
|||
|
err = UnpackPEFDataSection( (UInt8 *) packedDataSection, initSectionHeader->containerLength,
|
|||
|
(UInt8 *) unpackedDataSection, initSectionHeader->unpackedLength);
|
|||
|
}
|
|||
|
|
|||
|
// Extract the init routine's TVector from the unpacked section.
|
|||
|
|
|||
|
if (err == noErr) {
|
|||
|
BlockMoveData(unpackedDataSection + initOffset, &originalOffsets, sizeof(TVector));
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
MoreAssertQ(fragToFix->sectionHeaders[initSection].sectionKind == kPEFUnpackedDataSection);
|
|||
|
err = FSReadAtOffset(fragToFix->fileRef,
|
|||
|
fragToFix->locator.offset
|
|||
|
+ fragToFix->sectionHeaders[initSection].containerOffset
|
|||
|
+ initOffset,
|
|||
|
sizeof(TVector),
|
|||
|
&originalOffsets);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Step 3.
|
|||
|
|
|||
|
// Do the maths to subtract the unrelocated offsets from the current address
|
|||
|
// to get the base address.
|
|||
|
|
|||
|
if (err == noErr) {
|
|||
|
fragToFix->section0Base = ((char *) relocatedExport->codePtr) - (UInt32) originalOffsets.codePtr;
|
|||
|
fragToFix->section1Base = ((char *) relocatedExport->tocPtr) - (UInt32) originalOffsets.tocPtr;
|
|||
|
}
|
|||
|
|
|||
|
// Clean up.
|
|||
|
|
|||
|
if (packedDataSection != nil) {
|
|||
|
DisposePtr(packedDataSection);
|
|||
|
MoreAssertQ( MemError() == noErr );
|
|||
|
}
|
|||
|
if (unpackedDataSection != nil) {
|
|||
|
DisposePtr(unpackedDataSection);
|
|||
|
MoreAssertQ( MemError() == noErr );
|
|||
|
}
|
|||
|
return err;
|
|||
|
}
|
|||
|
|
|||
|
static void *GetSectionBaseAddress(const FragToFixInfo *fragToFix, UInt16 sectionIndex)
|
|||
|
// This routine returns the base of the instantiated section
|
|||
|
// whose index is sectionIndex. This routine is the evil twin
|
|||
|
// of SetupSectionBaseAddresses. It simply returns the values
|
|||
|
// for section 0 and 1 that we derived in SetupSectionBaseAddresses.
|
|||
|
// In a real implementation, this routine would call CFM API
|
|||
|
// to get this information, and SetupSectionBaseAddresses would
|
|||
|
// not exist, but CFM does not export the necessary APIs to
|
|||
|
// third parties.
|
|||
|
{
|
|||
|
void *result;
|
|||
|
|
|||
|
MoreAssertQ(fragToFix != nil);
|
|||
|
MoreAssertQ(fragToFix->containerHeader.tag1 == kPEFTag1);
|
|||
|
|
|||
|
switch (sectionIndex) {
|
|||
|
case 0:
|
|||
|
result = fragToFix->section0Base;
|
|||
|
break;
|
|||
|
case 1:
|
|||
|
result = fragToFix->section1Base;
|
|||
|
break;
|
|||
|
default:
|
|||
|
result = nil;
|
|||
|
break;
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static OSStatus FindImportLibrary(PEFLoaderInfoHeader *loaderSection, const char *libraryName, PEFImportedLibrary **importLibrary)
|
|||
|
// This routine finds the import library description (PEFImportedLibrary)
|
|||
|
// for the import library libraryName in the PEF loader section.
|
|||
|
// It sets *importLibrary to the address of the description.
|
|||
|
{
|
|||
|
OSStatus err;
|
|||
|
UInt32 librariesRemaining;
|
|||
|
PEFImportedLibrary *thisImportLibrary;
|
|||
|
Boolean found;
|
|||
|
|
|||
|
MoreAssertQ(loaderSection != nil);
|
|||
|
MoreAssertQ(libraryName != nil);
|
|||
|
MoreAssertQ(importLibrary != nil);
|
|||
|
|
|||
|
// Loop through each import library looking for a matching name.
|
|||
|
|
|||
|
// Initialise thisImportLibrary to point to the byte after the
|
|||
|
// end of the loader section's header.
|
|||
|
|
|||
|
thisImportLibrary = (PEFImportedLibrary *) (loaderSection + 1);
|
|||
|
librariesRemaining = loaderSection->importedLibraryCount;
|
|||
|
found = false;
|
|||
|
while ( librariesRemaining > 0 && ! found ) {
|
|||
|
// PEF defines that import library names will have
|
|||
|
// a null terminator, so we can just use strcmp.
|
|||
|
found = (strcmp( libraryName,
|
|||
|
((char *)loaderSection)
|
|||
|
+ loaderSection->loaderStringsOffset
|
|||
|
+ thisImportLibrary->nameOffset) == 0);
|
|||
|
// *** Remove ANSI strcmp eventually.
|
|||
|
if ( ! found ) {
|
|||
|
thisImportLibrary += 1;
|
|||
|
librariesRemaining -= 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (found) {
|
|||
|
*importLibrary = thisImportLibrary;
|
|||
|
err = noErr;
|
|||
|
} else {
|
|||
|
*importLibrary = nil;
|
|||
|
err = cfragNoLibraryErr;
|
|||
|
}
|
|||
|
return err;
|
|||
|
}
|
|||
|
|
|||
|
static OSStatus LookupSymbol(CFMLateImportLookupProc lookup, void *refCon,
|
|||
|
PEFLoaderInfoHeader *loaderSection,
|
|||
|
UInt32 symbolIndex,
|
|||
|
UInt32 *symbolValue)
|
|||
|
// This routine is used to look up a symbol during relocation.
|
|||
|
// "lookup" is a client callback and refCon is its argument.
|
|||
|
// Typically refCon is the CFM connection to the library that is
|
|||
|
// substituting for the weak linked library. loaderSection
|
|||
|
// is a pointer to the loader section of the fragment to fix up.
|
|||
|
// symbolIndex is the index of the imported symbol in the loader section.
|
|||
|
// The routine sets the word pointed to by symbolValue to the
|
|||
|
// value of the symbol.
|
|||
|
//
|
|||
|
// The routine works by using symbolIndex to index into the imported
|
|||
|
// symbol table to find the offset of the symbol's name in the string
|
|||
|
// table. It then looks up the symbol by calling the client's "lookup"
|
|||
|
// function and passes the resulting symbol address back in symbolValue.
|
|||
|
{
|
|||
|
OSStatus err;
|
|||
|
UInt32 *importSymbolTable;
|
|||
|
UInt32 symbolStringOffset;
|
|||
|
Boolean symbolIsWeak;
|
|||
|
CFragSymbolClass symbolClass;
|
|||
|
char *symbolStringAddress;
|
|||
|
Str255 symbolString;
|
|||
|
|
|||
|
MoreAssertQ(lookup != nil);
|
|||
|
MoreAssertQ(loaderSection != nil);
|
|||
|
MoreAssertQ(symbolIndex < loaderSection->totalImportedSymbolCount);
|
|||
|
MoreAssertQ(symbolValue != nil);
|
|||
|
|
|||
|
// Find the base of the imported symbol table.
|
|||
|
|
|||
|
importSymbolTable = (UInt32 *)(((char *)(loaderSection + 1)) + (loaderSection->importedLibraryCount * sizeof(PEFImportedLibrary)));
|
|||
|
|
|||
|
// Grab the appropriate entry out of the table and
|
|||
|
// extract the information from that entry.
|
|||
|
|
|||
|
symbolStringOffset = importSymbolTable[symbolIndex];
|
|||
|
symbolClass = PEFImportedSymbolClass(symbolStringOffset);
|
|||
|
symbolIsWeak = ((symbolClass & kPEFWeakImportSymMask) != 0);
|
|||
|
symbolClass = symbolClass & ~kPEFWeakImportSymMask;
|
|||
|
symbolStringOffset = PEFImportedSymbolNameOffset(symbolStringOffset);
|
|||
|
|
|||
|
// Find the string for the symbol in the strings table and
|
|||
|
// extract it from the table into a Pascal string on the stack.
|
|||
|
|
|||
|
symbolStringAddress = ((char *)loaderSection) + loaderSection->loaderStringsOffset + symbolStringOffset;
|
|||
|
symbolString[0] = strlen(symbolStringAddress); // *** remove ANSI strlen
|
|||
|
BlockMoveData(symbolStringAddress, &symbolString[1], symbolString[0]);
|
|||
|
|
|||
|
// Look up the symbol in substitute library. If it fails, return
|
|||
|
// a 0 value and check whether the error is fatal (a strong linked
|
|||
|
// symbol) or benign (a weak linked symbol).
|
|||
|
|
|||
|
err = lookup(symbolString, symbolClass, (void **) symbolValue, refCon);
|
|||
|
if (err != noErr) {
|
|||
|
*symbolValue = 0;
|
|||
|
if (symbolIsWeak) {
|
|||
|
err = noErr;
|
|||
|
}
|
|||
|
}
|
|||
|
return err;
|
|||
|
}
|
|||
|
|
|||
|
// The EngineState structure encapsulates all of the persistent state
|
|||
|
// of the CFM relocation engine virtual machine. I originally defined
|
|||
|
// this structure so I could pass the state around between routines
|
|||
|
// that implement various virtual opcodes, however I later worked
|
|||
|
// out that the relocation was sufficiently simple that I could put it
|
|||
|
// in in one routine. Still, I left the state in this structure in
|
|||
|
// case I ever need to reverse that decision. It's also a convenient
|
|||
|
// instructional design.
|
|||
|
|
|||
|
struct EngineState {
|
|||
|
UInt32 currentReloc; // Index of current relocation opcodes
|
|||
|
UInt32 terminatingReloc; // Index of relocation opcodes which terminates relocation
|
|||
|
UInt32 *sectionBase; // Start of the section
|
|||
|
UInt32 *relocAddress; // Address within the section where the relocations are to be performed
|
|||
|
UInt32 importIndex; // Symbol index, which is used to access an imported symbol's address
|
|||
|
void *sectionC; // Memory address of an instantiated section within the PEF container; this variable is used by relocation opcodes that relocate section addresses
|
|||
|
void *sectionD; // Memory address of an instantiated section within the PEF container; this variable is used by relocation opcodes that relocate section addresses
|
|||
|
};
|
|||
|
typedef struct EngineState EngineState;
|
|||
|
|
|||
|
// Note:
|
|||
|
// If I ever have to support the repeat opcodes, I'll probably
|
|||
|
// have to add a repeat counter to EngineState.
|
|||
|
|
|||
|
static OSStatus InitEngineState(const FragToFixInfo *fragToFix,
|
|||
|
UInt16 relocHeaderIndex,
|
|||
|
EngineState *state)
|
|||
|
// This routine initialises the engine state suitably for
|
|||
|
// running the relocation opcodes for the section whose
|
|||
|
// index is relocHeaderIndex. relocHeaderIndex is not a
|
|||
|
// a section number. See the comment where it's used below
|
|||
|
// for details. The routine basically fills out all the fields
|
|||
|
// in the EngineState structure as described by
|
|||
|
// "Mac OS Runtime Architectures".
|
|||
|
{
|
|||
|
OSStatus err;
|
|||
|
PEFLoaderRelocationHeader *relocHeader;
|
|||
|
|
|||
|
MoreAssertQ(fragToFix != nil);
|
|||
|
MoreAssertQ(state != nil);
|
|||
|
|
|||
|
// This bit is tricky. relocHeaderIndex is an index into the relocation
|
|||
|
// header table, starting at relocSectionCount (which is in the loader
|
|||
|
// section header) for the first relocated section and decrementing
|
|||
|
// down to 1 for the last relocated section. I find the relocation
|
|||
|
// header by using relocHeaderIndex as a index backwards from the
|
|||
|
// start of the relocation opcodes (ie relocInstrOffset). If you
|
|||
|
// look at the diagram of the layout of the container in
|
|||
|
// "PEFBinaryFormat.h", you'll see that the relocation opcodes
|
|||
|
// immediately follow the relocation headers.
|
|||
|
//
|
|||
|
// I did this because the alternative (starting at the loader
|
|||
|
// header and stepping past the import library table and the
|
|||
|
// import symbol table) was a pain.
|
|||
|
|
|||
|
relocHeader = (PEFLoaderRelocationHeader *) (((char *) fragToFix->loaderSection) + fragToFix->loaderSection->relocInstrOffset - relocHeaderIndex * sizeof(PEFLoaderRelocationHeader));
|
|||
|
|
|||
|
MoreAssertQ(relocHeader->reservedA == 0); // PEF spec says it must be; we check to try to catch bugs in calculation of relocHeader
|
|||
|
|
|||
|
state->currentReloc = relocHeader->firstRelocOffset;
|
|||
|
state->terminatingReloc = relocHeader->firstRelocOffset + relocHeader->relocCount;
|
|||
|
state->sectionBase = (UInt32 *) GetSectionBaseAddress(fragToFix, relocHeader->sectionIndex);
|
|||
|
state->relocAddress = state->sectionBase;
|
|||
|
state->importIndex = 0;
|
|||
|
|
|||
|
// From "Mac OS Runtime Architectures":
|
|||
|
//
|
|||
|
// The sectionC and sectionD variables actually contain the
|
|||
|
// memory address of an instantiated section minus the
|
|||
|
// default address for that section. The default address for a
|
|||
|
// section is contained in the defaultAddress field of the
|
|||
|
// section header. However, in almost all cases the default
|
|||
|
// address should be 0, so the simplified definition suffices.
|
|||
|
//
|
|||
|
// In the debug version, we drop into MacsBug if this weird case
|
|||
|
// ever executes because it's more likely we made a mistake than
|
|||
|
// we encountered a section with a default address.
|
|||
|
|
|||
|
state->sectionC = GetSectionBaseAddress(fragToFix, 0);
|
|||
|
if (state->sectionC != nil) {
|
|||
|
#if MORE_DEBUG
|
|||
|
if (fragToFix->sectionHeaders[0].defaultAddress != 0) {
|
|||
|
DebugStr("\pInitEngineState: Executing weird case.");
|
|||
|
}
|
|||
|
#endif
|
|||
|
(char *) state->sectionC -= fragToFix->sectionHeaders[0].defaultAddress;
|
|||
|
}
|
|||
|
state->sectionD = GetSectionBaseAddress(fragToFix, 1);
|
|||
|
if (state->sectionD != nil) {
|
|||
|
#if MORE_DEBUG
|
|||
|
if (fragToFix->sectionHeaders[1].defaultAddress != 0) {
|
|||
|
DebugStr("\pInitEngineState: Executing weird case.");
|
|||
|
}
|
|||
|
#endif
|
|||
|
(char *) state->sectionD -= fragToFix->sectionHeaders[1].defaultAddress;
|
|||
|
}
|
|||
|
|
|||
|
err = noErr;
|
|||
|
if (state->relocAddress == nil) {
|
|||
|
err = cfragFragmentUsageErr;
|
|||
|
}
|
|||
|
return err;
|
|||
|
}
|
|||
|
|
|||
|
// kPEFRelocBasicOpcodes is a table that maps the top 7 bits of the opcode
|
|||
|
// to a fundamental action. It's contents are defined for me in "PEFBinaryFormat.h",
|
|||
|
// which is really convenient.
|
|||
|
|
|||
|
static UInt8 kPEFRelocBasicOpcodes[kPEFRelocBasicOpcodeRange] = { PEFMaskedBasicOpcodes };
|
|||
|
|
|||
|
static OSStatus RunRelocationEngine(const FragToFixInfo *fragToFix,
|
|||
|
PEFImportedLibrary *importLibrary,
|
|||
|
CFMLateImportLookupProc lookup, void *refCon)
|
|||
|
// This is where the rubber really hits the. Given a fully
|
|||
|
// populated fragToFix structure, the import library description
|
|||
|
// of the weak imported library we're resolving, and a connection
|
|||
|
// to the library we're going to substitute it, re-execute the
|
|||
|
// relocation instructions (CFM has already executed them once)
|
|||
|
// but only *do* instructions (ie store the change to the data section)
|
|||
|
// that CFM skipped because the weak symbols were missing.
|
|||
|
{
|
|||
|
OSStatus err;
|
|||
|
EngineState state;
|
|||
|
UInt16 sectionsLeftToRelocate;
|
|||
|
UInt32 totalRelocs;
|
|||
|
UInt16 *relocInstrTable;
|
|||
|
UInt16 opCode;
|
|||
|
|
|||
|
MoreAssertQ(fragToFix != nil);
|
|||
|
MoreAssertQ(fragToFix->containerHeader.tag1 == kPEFTag1);
|
|||
|
MoreAssertQ(fragToFix->sectionHeaders != nil);
|
|||
|
MoreAssertQ(fragToFix->loaderSection != nil);
|
|||
|
MoreAssertQ(fragToFix->section0Base != nil); // Technically, having a nil for these two is not a problem, ...
|
|||
|
MoreAssertQ(fragToFix->section1Base != nil); // but in practise it a wildly deviant case and we should know about it.
|
|||
|
MoreAssertQ(importLibrary != nil);
|
|||
|
MoreAssertQ(lookup != nil);
|
|||
|
|
|||
|
// Before entering the loop, work out some information in advance.
|
|||
|
|
|||
|
// totalRelocs is only used for debugging, to make sure our
|
|||
|
// relocation PC (state.currentReloc) doesn't run wild.
|
|||
|
|
|||
|
totalRelocs = (fragToFix->loaderSection->loaderStringsOffset - fragToFix->loaderSection->relocInstrOffset) / sizeof(UInt16);
|
|||
|
|
|||
|
// relocInstrTable is the base address of the table of relocation
|
|||
|
// instructions in the fragment to fix.
|
|||
|
|
|||
|
relocInstrTable = (UInt16 *)((char *) fragToFix->loaderSection + fragToFix->loaderSection->relocInstrOffset);
|
|||
|
|
|||
|
// sectionsLeftToRelocate is the loop counter for the outer loop.
|
|||
|
|
|||
|
MoreAssertQ(fragToFix->loaderSection->relocSectionCount <= 0x0FFFF);
|
|||
|
sectionsLeftToRelocate = fragToFix->loaderSection->relocSectionCount;
|
|||
|
|
|||
|
// Now let's run the relocation engine. We run it once per
|
|||
|
// section in the table. Each time around, we init the engine
|
|||
|
// and then loop again, this time executing individual opcodes.
|
|||
|
// The opcode loop terminates when the relocation PC
|
|||
|
// (state.currentReloc) hits the final opcode (state.terminatingReloc).
|
|||
|
|
|||
|
// Note:
|
|||
|
// One design decision I made was to totally re-init the engine state
|
|||
|
// for each section. The CFM spec is unclear as to whether you're supposed
|
|||
|
// to totally re-init the engine state, or just re-init the section-specific
|
|||
|
// state (ie currentReloc, terminatingReloc, and relocAddress). I hope this
|
|||
|
// is correct, but it's hard to test without having a fragment with multiple
|
|||
|
// relocated sections, which is difficult to create.
|
|||
|
|
|||
|
// How do I decide which opcodes should be effective (ie make changes to
|
|||
|
// the section being relocated) and which opcodes should just be executed
|
|||
|
// for their side effects (ie updated state.relocAddress or state.importIndex)?
|
|||
|
// The answer is both simple and subtle. Opcodes whose actions are dependent
|
|||
|
// on a symbol that was in the weak linked library are effective, those that
|
|||
|
// an independent of those symbols are not. The only opcodes that use
|
|||
|
// symbolic values are kPEFRelocImportRun and kPEFRelocSmByImport, and
|
|||
|
// these are only if the symbol is in the weak linked library.
|
|||
|
// All other cases are executed for their side effects only.
|
|||
|
//
|
|||
|
// How do I determine if a symbol is in the weak linked library?
|
|||
|
// Well I know the symbol's index and I know the lower bound and count
|
|||
|
// of the symbols in the weak linked library, so I just do a simple
|
|||
|
// bounds test, ie
|
|||
|
//
|
|||
|
// firstImportedSymbol <= importIndex < firstImportedSymbol + importedSymbolCount
|
|||
|
|
|||
|
// From this code, it's relatively easy to see which relocation opcodes
|
|||
|
// aren't implemented. If you ever encounter one, you'll find yourself
|
|||
|
// in MacsBug with a message telling you which opcode was found. The
|
|||
|
// two big groups of opcodes I skipped were the large format opcodes
|
|||
|
// and the repeating opcodes. I skipped them because:
|
|||
|
//
|
|||
|
// a) I haven't got a way to generate them in a PEF container that I can
|
|||
|
// test against. Without that, there's no way I could be assured that
|
|||
|
// the code worked.
|
|||
|
//
|
|||
|
// b) I'm lazy.
|
|||
|
|
|||
|
err = noErr;
|
|||
|
while ( sectionsLeftToRelocate > 0 ) {
|
|||
|
err = InitEngineState(fragToFix, sectionsLeftToRelocate, &state);
|
|||
|
if (err != noErr) {
|
|||
|
goto leaveNow;
|
|||
|
}
|
|||
|
|
|||
|
while ( state.currentReloc != state.terminatingReloc ) {
|
|||
|
|
|||
|
MoreAssertQ( state.currentReloc < totalRelocs );
|
|||
|
|
|||
|
opCode = relocInstrTable[state.currentReloc];
|
|||
|
switch ( PEFRelocBasicOpcode(opCode) ) {
|
|||
|
case kPEFRelocBySectDWithSkip:
|
|||
|
{
|
|||
|
UInt16 skipCount;
|
|||
|
UInt16 relocCount;
|
|||
|
|
|||
|
skipCount = ((opCode >> 6) & 0x00FF);
|
|||
|
relocCount = (opCode & 0x003F);
|
|||
|
state.relocAddress += skipCount;
|
|||
|
state.relocAddress += relocCount;
|
|||
|
}
|
|||
|
break;
|
|||
|
case kPEFRelocBySectC:
|
|||
|
case kPEFRelocBySectD:
|
|||
|
{
|
|||
|
UInt16 runLength;
|
|||
|
|
|||
|
runLength = (opCode & 0x01FF) + 1;
|
|||
|
state.relocAddress += runLength;
|
|||
|
}
|
|||
|
break;
|
|||
|
case kPEFRelocTVector12:
|
|||
|
{
|
|||
|
UInt16 runLength;
|
|||
|
|
|||
|
runLength = (opCode & 0x01FF) + 1;
|
|||
|
state.relocAddress += (runLength * 3);
|
|||
|
}
|
|||
|
break;
|
|||
|
case kPEFRelocTVector8:
|
|||
|
case kPEFRelocVTable8:
|
|||
|
{
|
|||
|
UInt16 runLength;
|
|||
|
|
|||
|
runLength = (opCode & 0x01FF) + 1;
|
|||
|
state.relocAddress += (runLength * 2);
|
|||
|
}
|
|||
|
break;
|
|||
|
case kPEFRelocImportRun:
|
|||
|
{
|
|||
|
UInt32 symbolValue;
|
|||
|
UInt16 runLength;
|
|||
|
|
|||
|
runLength = (opCode & 0x01FF) + 1;
|
|||
|
while (runLength > 0) {
|
|||
|
if ( state.importIndex >= importLibrary->firstImportedSymbol && state.importIndex < (importLibrary->firstImportedSymbol + importLibrary->importedSymbolCount) ) {
|
|||
|
err = LookupSymbol(lookup, refCon, fragToFix->loaderSection, state.importIndex, &symbolValue);
|
|||
|
if (err != noErr) {
|
|||
|
goto leaveNow;
|
|||
|
}
|
|||
|
*(state.relocAddress) += symbolValue;
|
|||
|
}
|
|||
|
state.importIndex += 1;
|
|||
|
state.relocAddress += 1;
|
|||
|
runLength -= 1;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
case kPEFRelocSmByImport:
|
|||
|
{
|
|||
|
UInt32 symbolValue;
|
|||
|
UInt32 index;
|
|||
|
|
|||
|
index = (opCode & 0x01FF);
|
|||
|
if ( index >= importLibrary->firstImportedSymbol && index < (importLibrary->firstImportedSymbol + importLibrary->importedSymbolCount) ) {
|
|||
|
err = LookupSymbol(lookup, refCon, fragToFix->loaderSection, index, &symbolValue);
|
|||
|
if (err != noErr) {
|
|||
|
goto leaveNow;
|
|||
|
}
|
|||
|
*(state.relocAddress) += symbolValue;
|
|||
|
}
|
|||
|
state.importIndex = index + 1;
|
|||
|
state.relocAddress += 1;
|
|||
|
}
|
|||
|
break;
|
|||
|
case kPEFRelocSmSetSectC:
|
|||
|
{
|
|||
|
UInt32 index;
|
|||
|
|
|||
|
index = (opCode & 0x01FF);
|
|||
|
state.sectionC = GetSectionBaseAddress(fragToFix, index);
|
|||
|
MoreAssertQ(state.sectionC != nil);
|
|||
|
}
|
|||
|
break;
|
|||
|
case kPEFRelocSmSetSectD:
|
|||
|
{
|
|||
|
UInt32 index;
|
|||
|
|
|||
|
index = (opCode & 0x01FF);
|
|||
|
state.sectionD = GetSectionBaseAddress(fragToFix, index);
|
|||
|
MoreAssertQ(state.sectionD != nil);
|
|||
|
}
|
|||
|
break;
|
|||
|
case kPEFRelocSmBySection:
|
|||
|
state.relocAddress += 1;
|
|||
|
break;
|
|||
|
case kPEFRelocIncrPosition:
|
|||
|
{
|
|||
|
UInt16 offset;
|
|||
|
|
|||
|
offset = (opCode & 0x0FFF) + 1;
|
|||
|
((char *) state.relocAddress) += offset;
|
|||
|
}
|
|||
|
break;
|
|||
|
case kPEFRelocSmRepeat:
|
|||
|
#if MORE_DEBUG
|
|||
|
DebugStr("\pRunRelocationEngine: kPEFRelocSmRepeat not yet implemented");
|
|||
|
#endif
|
|||
|
err = unimpErr;
|
|||
|
goto leaveNow;
|
|||
|
break;
|
|||
|
case kPEFRelocSetPosition:
|
|||
|
{
|
|||
|
UInt32 offset;
|
|||
|
|
|||
|
// Lot's of folks have tried various interpretations of the description of
|
|||
|
// this opCode in "Mac OS Runtime Architectures" (which states "This instruction
|
|||
|
// sets relocAddress to the address of the section offset offset." *smile*).
|
|||
|
// I eventually dug into the CFM source code to find my interpretation, which
|
|||
|
// I believe is correct. The key point is tht the offset is relative to
|
|||
|
// the start of the section for which these relocations are being performed.
|
|||
|
|
|||
|
// Skip to next reloc word, which is the second chunk of the offset.
|
|||
|
|
|||
|
state.currentReloc += 1;
|
|||
|
|
|||
|
// Extract offset based on the most significant 10 bits in opCode and
|
|||
|
// the next significant 16 bits in the next reloc word.
|
|||
|
|
|||
|
offset = PEFRelocSetPosFullOffset(opCode, relocInstrTable[state.currentReloc]);
|
|||
|
|
|||
|
state.relocAddress = (UInt32 *) ( ((char *) state.sectionBase) + offset);
|
|||
|
}
|
|||
|
break;
|
|||
|
case kPEFRelocLgByImport:
|
|||
|
{
|
|||
|
UInt32 symbolValue;
|
|||
|
UInt32 index;
|
|||
|
|
|||
|
// Get the 26 bit symbol index from the current and next reloc words.
|
|||
|
|
|||
|
state.currentReloc += 1;
|
|||
|
index = PEFRelocLgByImportFullIndex(opCode, relocInstrTable[state.currentReloc]);
|
|||
|
|
|||
|
if ( index >= importLibrary->firstImportedSymbol && index < (importLibrary->firstImportedSymbol + importLibrary->importedSymbolCount) ) {
|
|||
|
err = LookupSymbol(lookup, refCon, fragToFix->loaderSection, index, &symbolValue);
|
|||
|
if (err != noErr) {
|
|||
|
goto leaveNow;
|
|||
|
}
|
|||
|
*(state.relocAddress) += symbolValue;
|
|||
|
}
|
|||
|
state.importIndex = index + 1;
|
|||
|
state.relocAddress += 1;
|
|||
|
}
|
|||
|
break;
|
|||
|
case kPEFRelocLgRepeat:
|
|||
|
#if MORE_DEBUG
|
|||
|
DebugStr("\pRunRelocationEngine: kPEFRelocLgRepeat not yet implemented");
|
|||
|
#endif
|
|||
|
err = unimpErr;
|
|||
|
goto leaveNow;
|
|||
|
break;
|
|||
|
case kPEFRelocLgSetOrBySection:
|
|||
|
#if MORE_DEBUG
|
|||
|
DebugStr("\pRunRelocationEngine: kPEFRelocLgSetOrBySection not yet implemented");
|
|||
|
#endif
|
|||
|
err = unimpErr;
|
|||
|
goto leaveNow;
|
|||
|
break;
|
|||
|
case kPEFRelocUndefinedOpcode:
|
|||
|
err = cfragFragmentCorruptErr;
|
|||
|
goto leaveNow;
|
|||
|
break;
|
|||
|
default:
|
|||
|
MoreAssertQ(false);
|
|||
|
err = cfragFragmentCorruptErr;
|
|||
|
goto leaveNow;
|
|||
|
break;
|
|||
|
}
|
|||
|
state.currentReloc += 1;
|
|||
|
}
|
|||
|
|
|||
|
sectionsLeftToRelocate -= 1;
|
|||
|
}
|
|||
|
|
|||
|
leaveNow:
|
|||
|
return err;
|
|||
|
}
|
|||
|
|
|||
|
extern pascal OSStatus CFMLateImportCore(const CFragSystem7DiskFlatLocator *fragToFixLocator,
|
|||
|
CFragConnectionID fragToFixConnID,
|
|||
|
CFragInitFunction fragToFixInitRoutine,
|
|||
|
ConstStr255Param weakLinkedLibraryName,
|
|||
|
CFMLateImportLookupProc lookup,
|
|||
|
void *refCon)
|
|||
|
// See comments in interface part.
|
|||
|
{
|
|||
|
OSStatus err;
|
|||
|
OSStatus junk;
|
|||
|
FragToFixInfo fragToFix;
|
|||
|
PEFImportedLibrary *importLibrary;
|
|||
|
char weakLinkedLibraryNameCString[256];
|
|||
|
|
|||
|
MoreAssertQ(fragToFixLocator != nil);
|
|||
|
MoreAssertQ(fragToFixConnID != nil);
|
|||
|
MoreAssertQ(fragToFixInitRoutine != nil);
|
|||
|
MoreAssertQ(weakLinkedLibraryName != nil);
|
|||
|
MoreAssertQ(lookup != nil);
|
|||
|
|
|||
|
// Fill out the bits of fragToFix which are passed in
|
|||
|
// by the client.
|
|||
|
|
|||
|
MoreBlockZero(&fragToFix, sizeof(fragToFix));
|
|||
|
fragToFix.locator = *fragToFixLocator;
|
|||
|
fragToFix.connID = fragToFixConnID;
|
|||
|
fragToFix.initRoutine = fragToFixInitRoutine;
|
|||
|
|
|||
|
// Make a C string from weakLinkedLibraryName.
|
|||
|
|
|||
|
BlockMoveData(weakLinkedLibraryName + 1, weakLinkedLibraryNameCString, weakLinkedLibraryName[0]);
|
|||
|
weakLinkedLibraryNameCString[weakLinkedLibraryName[0]] = 0;
|
|||
|
|
|||
|
// Get the basic information from the fragment.
|
|||
|
// Fills out the containerHeader, sectionHeaders, loaderSection and fileRef fields
|
|||
|
// of fragToFix.
|
|||
|
|
|||
|
err = ReadContainerBasics(&fragToFix);
|
|||
|
|
|||
|
// Set up the base address fields in fragToFix (ie section0Base and section1Base)
|
|||
|
// by looking up our init routine (fragToFix.initRoutine) and subtracting
|
|||
|
// away the section offsets (which we get from the disk copy of the section)
|
|||
|
// to derive the bases of the sections themselves.
|
|||
|
|
|||
|
if (err == noErr) {
|
|||
|
err = SetupSectionBaseAddresses(&fragToFix);
|
|||
|
}
|
|||
|
|
|||
|
// Look inside the loader section for the import library description
|
|||
|
// of weakLinkedLibraryName. We need this to know the range of symbol
|
|||
|
// indexes we're going to fix up.
|
|||
|
|
|||
|
if (err == noErr) {
|
|||
|
err = FindImportLibrary(fragToFix.loaderSection, weakLinkedLibraryNameCString, &importLibrary);
|
|||
|
}
|
|||
|
|
|||
|
// Do a quick check to ensure that the library was actually imported weak.
|
|||
|
// If it wasn't, it doesn't make much sense to resolve its weak imports
|
|||
|
// later on. Resolving them again is likely to be bad.
|
|||
|
|
|||
|
if (err == noErr) {
|
|||
|
if ((importLibrary->options & kPEFWeakImportLibMask) == 0) {
|
|||
|
err = cfragFragmentUsageErr;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Now run the main relocation engine.
|
|||
|
|
|||
|
if (err == noErr) {
|
|||
|
err = RunRelocationEngine(&fragToFix, importLibrary, lookup, refCon);
|
|||
|
}
|
|||
|
|
|||
|
// Clean up.
|
|||
|
|
|||
|
if (fragToFix.disposeSectionPointers) {
|
|||
|
if (fragToFix.fileRef != 0) {
|
|||
|
junk = FSClose(fragToFix.fileRef);
|
|||
|
MoreAssertQ(junk == noErr);
|
|||
|
}
|
|||
|
if (fragToFix.loaderSection != nil) {
|
|||
|
DisposePtr( (Ptr) fragToFix.loaderSection);
|
|||
|
MoreAssertQ(MemError() == noErr);
|
|||
|
}
|
|||
|
if (fragToFix.sectionHeaders != nil) {
|
|||
|
DisposePtr( (Ptr) fragToFix.sectionHeaders);
|
|||
|
MoreAssertQ(MemError() == noErr);
|
|||
|
}
|
|||
|
}
|
|||
|
return err;
|
|||
|
}
|
|||
|
|
|||
|
static pascal OSStatus FragmentLookup(ConstStr255Param symName, CFragSymbolClass symClass,
|
|||
|
void **symAddr, void *refCon)
|
|||
|
// This is the CFMLateImportLookupProc callback used when
|
|||
|
// late importing from a CFM shared library.
|
|||
|
{
|
|||
|
OSStatus err;
|
|||
|
CFragConnectionID connIDToImport;
|
|||
|
CFragSymbolClass foundSymClass;
|
|||
|
|
|||
|
MoreAssertQ(symName != nil);
|
|||
|
MoreAssertQ(symAddr != nil);
|
|||
|
MoreAssertQ(refCon != nil);
|
|||
|
|
|||
|
connIDToImport = (CFragConnectionID) refCon;
|
|||
|
|
|||
|
// Shame there's no way to validate that connIDToImport is valid.
|
|||
|
|
|||
|
err = FindSymbol(connIDToImport, symName, (Ptr *) symAddr, &foundSymClass);
|
|||
|
if (err == noErr) {
|
|||
|
// If the symbol isn't of the right class, we act like we didn't
|
|||
|
// find it, but also assert in the debug build because weird things
|
|||
|
// are afoot.
|
|||
|
if (foundSymClass != symClass) {
|
|||
|
MoreAssertQ(false);
|
|||
|
*symAddr = nil;
|
|||
|
err = cfragNoSymbolErr;
|
|||
|
}
|
|||
|
}
|
|||
|
return err;
|
|||
|
}
|
|||
|
|
|||
|
extern pascal OSStatus CFMLateImportLibrary(const CFragSystem7DiskFlatLocator *fragToFixLocator,
|
|||
|
CFragConnectionID fragToFixConnID,
|
|||
|
CFragInitFunction fragToFixInitRoutine,
|
|||
|
ConstStr255Param weakLinkedLibraryName,
|
|||
|
CFragConnectionID connIDToImport)
|
|||
|
// See comments in interface part.
|
|||
|
{
|
|||
|
MoreAssertQ(connIDToImport != nil);
|
|||
|
return CFMLateImportCore(fragToFixLocator, fragToFixConnID, fragToFixInitRoutine,
|
|||
|
weakLinkedLibraryName, FragmentLookup, connIDToImport);
|
|||
|
}
|
|||
|
|
|||
|
static pascal OSStatus BundleLookup(ConstStr255Param symName, CFragSymbolClass symClass,
|
|||
|
void **symAddr, void *refCon)
|
|||
|
// This is the CFMLateImportLookupProc callback used when
|
|||
|
// late importing from a CFBundle.
|
|||
|
{
|
|||
|
OSStatus err;
|
|||
|
CFBundleRef bundleToImport;
|
|||
|
CFStringRef symNameStr;
|
|||
|
|
|||
|
MoreAssertQ(symName != nil);
|
|||
|
MoreAssertQ(symAddr != nil);
|
|||
|
MoreAssertQ(refCon != nil);
|
|||
|
|
|||
|
symNameStr = nil;
|
|||
|
|
|||
|
bundleToImport = (CFBundleRef) refCon;
|
|||
|
|
|||
|
// Shame there's no way to validate that bundleToImport is really a bundle.
|
|||
|
|
|||
|
// We can only find function pointers because CFBundleGetFunctionPointerForName
|
|||
|
// only works for function pointers. So if the client is asking for something
|
|||
|
// other than a function pointer (ie TVector symbol) then we don't even true.
|
|||
|
// Also assert in the debug build because this shows a certain lack of
|
|||
|
// understanding on the part of the client.
|
|||
|
//
|
|||
|
// CF is being revise to support accessing data symbols using a new API
|
|||
|
// (currently this is available to Apple internal developers as
|
|||
|
// CFBundleGetDataPointerForName). When the new API is available in a
|
|||
|
// public header file I should revise this code to lift this restriction.
|
|||
|
|
|||
|
err = noErr;
|
|||
|
if (symClass != kTVectorCFragSymbol) {
|
|||
|
MoreAssertQ(false);
|
|||
|
err = cfragNoSymbolErr;
|
|||
|
}
|
|||
|
if (err == noErr) {
|
|||
|
symNameStr = CFStringCreateWithPascalString(kCFAllocatorSystemDefault,
|
|||
|
symName, kCFStringEncodingMacRoman);
|
|||
|
if (symNameStr == nil) {
|
|||
|
err = coreFoundationUnknownErr;
|
|||
|
}
|
|||
|
}
|
|||
|
if (err == noErr) {
|
|||
|
*symAddr = CFBundleGetFunctionPointerForName(bundleToImport, symNameStr);
|
|||
|
if (*symAddr == nil) {
|
|||
|
err = cfragNoSymbolErr;
|
|||
|
}
|
|||
|
}
|
|||
|
if (symNameStr != nil) {
|
|||
|
CFRelease(symNameStr);
|
|||
|
}
|
|||
|
return err;
|
|||
|
}
|
|||
|
|
|||
|
extern pascal OSStatus CFMLateImportBundle(const CFragSystem7DiskFlatLocator *fragToFixLocator,
|
|||
|
CFragConnectionID fragToFixConnID,
|
|||
|
CFragInitFunction fragToFixInitRoutine,
|
|||
|
ConstStr255Param weakLinkedLibraryName,
|
|||
|
CFBundleRef bundleToImport)
|
|||
|
// See comments in interface part.
|
|||
|
{
|
|||
|
MoreAssertQ(bundleToImport != nil);
|
|||
|
return CFMLateImportCore(fragToFixLocator, fragToFixConnID, fragToFixInitRoutine,
|
|||
|
weakLinkedLibraryName, BundleLookup, bundleToImport);
|
|||
|
}
|