home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OpenStep 4.2J (Developer)
/
os42jdev.iso
/
NextDeveloper
/
Source
/
GNU
/
debug
/
Common
/
RegionManager.m
< prev
next >
Wrap
Text File
|
1994-11-15
|
15KB
|
572 lines
#import "RegionManager.h"
#import <stdlib.h>
#import <string.h>
#import <mach/mach.h>
#import <mach-o/loader.h>
#import <mach-o/nlist.h>
#import <mach/vm_param.h>
kern_return_t static inline vmRegion(task_t task,
vm_address_t address,
Region *region)
{
vm_offset_t offset;
vm_prot_t maxProtection;
vm_inherit_t inheritance;
boolean_t shared;
port_t objectName;
region->reloc.address = address;
return vm_region(task,
®ion->reloc.address,
®ion->reloc.size,
®ion->protection,
&maxProtection,
&inheritance,
&shared,
&objectName,
&offset);
}
@implementation RegionManager
-initTask: (vm_task_t)theTask readInRegions: (BOOL)readInRegions
{
vm_statistics_data_t vm_stats;
task = theTask;
vm_statistics(theTask, &vm_stats);
pageSize = vm_stats.pagesize;
relocSize = sizeof(Region);
rmFlags.shouldSortRelocs = NO;
if (readInRegions)
[self readInAllRelocs];
else
rmFlags.invalid = YES;
return self;
}
-(BOOL)isTask
{
struct task_basic_info taskInfo;
unsigned int taskInfoCount;
BOOL ret;
taskInfoCount = TASK_BASIC_INFO_COUNT;
ret = (task_info(task,
TASK_BASIC_INFO,
(task_info_t)&taskInfo,
&taskInfoCount) == KERN_SUCCESS) ? YES : NO;
if (!ret && taskGoneCallBack)
(*taskGoneCallBack)();
return ret;
}
-(void)setTaskGoneCallBack: (TaskGoneCallBack)theCallBack
{
taskGoneCallBack = theCallBack;
}
+newTask: (vm_task_t)theTask
{
return [[super new] initTask: theTask readInRegions: YES];
}
+newTask: (vm_task_t)theTask readInRegions: (BOOL)readInRegions
{
return [[super new] initTask: theTask readInRegions: readInRegions];
}
-invalidate
{
if (rmFlags.invalid)
return self;
else {
Region *region;
int count;
if (relocs) {
for (count = numRelocs, region = (Region *)relocs;
count;
count--, region++) {
if (region->reloc.rFlags.readIn)
vm_deallocate(task_self(),
region->reloc.data,
region->reloc.size);
}
}
if (pages) {
free(pages);
pages = NULL;
}
return [super invalidate];
}
}
-(void)getStartPage: (void **)startPage andSize: (int *)sizePage
forPointer: (void *)start andSize: (int)numBytes
{
vm_address_t offset = ((vm_address_t)start % pageSize);
vm_address_t s = numBytes + offset;
*startPage = start - offset;
*sizePage = (s - (s % pageSize)) + pageSize;
}
-(void)protectDataAt: (const void *)start for: (int)numBytes
{
vm_address_t offset = ((vm_address_t)start % pageSize);
vm_address_t startPage = (vm_address_t)start - offset;
size_t sizePage = numBytes + offset;
vm_protect(task, startPage, pageSize, NO, VM_PROT_READ);
}
-(void)unProtectDataAt: (const void *)start for: (int)numBytes
{
vm_address_t offset = ((vm_address_t)start % pageSize);
vm_address_t startPage = (vm_address_t)start - offset;
size_t sizePage = numBytes + offset;
vm_protect(task, startPage, pageSize, NO, VM_PROT_READ | VM_PROT_WRITE);
}
-(int)writeDataAt: (const void *)start for: (int)numBytes reloc: (Region *)region markOnly: (BOOL)markOnly
{
vm_address_t offset = ((vm_address_t)start % pageSize);
vm_address_t startPage = (vm_address_t)start - offset;
size_t sizePage = numBytes + offset;
pointer_t startData;
kern_return_t ret = 0;
unsigned char *pPage;
if (markOnly) {
if (!region->pagesInvalid)
regionsInvalid++;
}
for (sizePage += (pageSize - (sizePage % pageSize)),
startData = region->reloc.data
+ ((pointer_t)startPage - region->reloc.address),
pPage = region->pages
+ (((pointer_t)startPage - region->reloc.address) / pageSize);
sizePage && !ret;
sizePage -= pageSize, startData += pageSize, startPage += pageSize,
pPage++) {
if (markOnly) {
if (!(*pPage & PAGEINVALID)) {
*pPage |= PAGEINVALID;
region->pagesInvalid++;
}
} else {
*pPage &= ~PAGEINVALID;
if (*pPage & VM_PROT_WRITE)
ret = vm_write(task, startPage, startData, pageSize);
else {
vm_protect(task, startPage, pageSize, NO, VM_PROT_WRITE);
ret = vm_write(task, startPage, startData, pageSize);
vm_protect(task, startPage, pageSize, NO,
(vm_prot_t)(*pPage & 0x7));
}
}
}
if (ret == KERN_SUCCESS)
return numBytes;
else {
[self isTask];
return 0;
}
}
-(int)writeDataAt: (const void *)start for: (int)numBytes reloc: (Region *)region
{
return [self writeDataAt: start for: numBytes reloc: region markOnly: NO];
}
-(int)putDataAt: (void *)start for: (int)numBytes from: (const void *)data markOnly: (BOOL)markOnly
{
int numBytesInRegion;
Reloc *reloc = [self relocFor: start];
if (reloc) {
numBytesInRegion = reloc->maxAddress - (int)start;
if (numBytes > numBytesInRegion)
numBytes = numBytesInRegion;
if (numBytes == 1)
*(char *)(reloc->data + ((pointer_t)start - reloc->address))
= *(char *)data;
else
memcpy((void *)(reloc->data + ((pointer_t)start - reloc->address)),
data,
numBytes);
return [self writeDataAt: start for: numBytes reloc: (Region *)reloc markOnly: markOnly];
} else
return 0;
}
-(int)putDataAt: (void *)start for: (int)numBytes from: (const void *)data
{
return [self putDataAt: start for: numBytes from: data markOnly: NO];
}
-(void)flushMarkedPages
{
int ret = 0;
while (regionsInvalid) {
int i;
Region *region;
for (i = 0, region = (Region *)relocs;
i < numRelocs;
i++, ((void *)region) += relocSize) {
if (region->pagesInvalid) {
unsigned char *p = region->pages;
while (region->pagesInvalid) {
if (*p & PAGEINVALID) {
int numPage = p - region->pages;
pointer_t startPage, startData;
startPage
= region->reloc.address + (numPage * pageSize);
startData
= region->reloc.data + (numPage * pageSize);
*p &= ~PAGEINVALID;
region->pagesInvalid--;
if (*p & VM_PROT_WRITE)
ret = vm_write(task, startPage, startData, pageSize);
else {
vm_protect(task, startPage, pageSize, NO, VM_PROT_WRITE);
ret = vm_write(task, startPage, startData, pageSize);
vm_protect(task, startPage, pageSize, NO,
(vm_prot_t)(*p & 0x7));
}
}
p++;
}
regionsInvalid--;
}
}
}
if (ret)
[self isTask];
}
-(int)writeDataAt: (const void *)start for: (int)numBytes;
{
Reloc *reloc = [self relocFor: start];
if (reloc)
return [self writeDataAt: start for: numBytes reloc: (Region *)reloc];
else
return NO;
}
-(BOOL)readInReloc: (Reloc *)reloc
{
kern_return_t ret;
unsigned int nData;
if (reloc->rFlags.readIn) {
return YES;
} else {
ret = vm_read(task, reloc->address, reloc->size, &reloc->data, &nData);
if (ret == KERN_SUCCESS) {
reloc->maxData = reloc->data + reloc->size;
reloc->displacement = reloc->data - reloc->address;
reloc->rFlags.readIn = YES;
return YES;
} else {
[self isTask];
return NO;
}
}
}
-(void)combineRegions: (BOOL)combineRegions
{ dontCombineRegions = !combineRegions;}
-(void)_readInAllRelocs
{
kern_return_t error = 0;
vm_address_t address = VM_MIN_ADDRESS;
Region *theRegion, *regions, *newRegion, *nextRegion, *lastRegion;
int count = 0, numPages = 0, numAlloced = 16, nPages;
unsigned char *thePage;
if ([self isTask]) {
theRegion = regions = malloc(numAlloced * sizeof(Region));
bzero(regions, numAlloced * sizeof(Region));
while (!error) {
error = vmRegion(task, address, theRegion);
address = theRegion->reloc.address + theRegion->reloc.size;
if (!error) {
if (theRegion->protection & VM_PROT_READ) {
count++;
numPages += theRegion->reloc.size / pageSize;
if (count < numAlloced)
theRegion++;
else {
numAlloced *= 2;
regions = realloc(regions,
numAlloced * sizeof(Region));
theRegion = regions + count;
bzero(theRegion, count * sizeof(Region));
}
}
if (address < theRegion->reloc.address)
error = KERN_NO_SPACE;
}
}
if (error == KERN_NO_SPACE) {
lastRegion = theRegion;
pages = malloc(numPages);
for (theRegion = regions, thePage = pages;
theRegion < lastRegion;
theRegion++) {
theRegion->pages = thePage;
nPages = theRegion->reloc.size / pageSize;
while(nPages--)
*(thePage++) = (unsigned char)theRegion->protection;
}
if (dontCombineRegions) {
for (theRegion = regions; theRegion < lastRegion; theRegion++)
theRegion->reloc.maxAddress
= theRegion->reloc.address + theRegion->reloc.size;
relocs = regions;
numRelocs = count;
} else {
relocs = malloc(count * sizeof(Region));
bzero(relocs, count * sizeof(Region));
for (theRegion = regions, newRegion = relocs;
theRegion < lastRegion;
theRegion = nextRegion, newRegion++) {
for (nextRegion = theRegion + 1;
(nextRegion < lastRegion
&& ((theRegion->reloc.address + theRegion->reloc.size)
== nextRegion->reloc.address));
nextRegion++) {
theRegion->reloc.size += nextRegion->reloc.size;
}
*newRegion = *theRegion;
newRegion->reloc.maxAddress
= newRegion->reloc.address + newRegion->reloc.size;
}
numRelocs = newRegion - (Region *)relocs;
relocs = realloc(relocs, numRelocs * sizeof(Region));
free(regions);
}
rmFlags.invalid = NO;
} else
free(regions);
if (error != KERN_NO_SPACE)
[self isTask];
}
}
-(struct mach_header *)getMachHeader
{
Reloc *reloc;
int count;
struct mach_header *header, *foundHeader;
if (rmFlags.invalid)
[self readInAllRelocs];
reloc = relocs;
for (foundHeader = NULL, count = 0;
!foundHeader && count < numRelocs;
count++) {
[self readInReloc: reloc];
header = [self pointerFor: (void *)reloc->address
withSize: sizeof(*header)];
if (header
&& (header->magic == MH_MAGIC)
&& ([self pointerFor: (void *)reloc->address
withSize: header->sizeofcmds]))
foundHeader = header;
else
((void *)reloc) += relocSize;
}
return foundHeader;
}
-(int)getNumMachHeaders
{
struct mach_header *myHeader;
int numHeaders, i;
struct fvmlib_command *loadCmd;
if (myHeader = [self getMachHeader]) {
numHeaders = 1;
for (i = 0, loadCmd = (struct fvmlib_command *)(myHeader + 1);
i < myHeader->ncmds;
i++,
loadCmd = ((void *)loadCmd + loadCmd->cmdsize)) {
if (loadCmd->cmd == LC_LOADFVMLIB)
numHeaders++;
}
} else
numHeaders = 0;
return numHeaders;
}
-(struct mach_header **)getMachHeadersWithNames: (char ***)names
{
struct mach_header *myHeader, **headers, *header;
char **theNames = NULL;
struct fvmlib_command *loadCmd;
int numHeaders, headerIndex;
numHeaders = [self getNumMachHeaders];
myHeader = [self getMachHeader];
headers = malloc((numHeaders + 1) * sizeof(*headers));
if (names) {
*names = theNames = malloc((numHeaders + 1) * sizeof(*theNames));
theNames[0] = NULL;
}
headers[0] = myHeader;
for (headerIndex = 1, loadCmd = (struct fvmlib_command *)(myHeader + 1);
headerIndex < numHeaders;
loadCmd = ((void *)loadCmd + loadCmd->cmdsize)) {
if (loadCmd->cmd == LC_LOADFVMLIB) {
header = [self pointerFor: (void *)(loadCmd->fvmlib.header_addr)
withSize: sizeof(*header)];
if (header)
headers[headerIndex]
= [self pointerFor: (void *)(loadCmd->fvmlib.header_addr)
withSize: header->sizeofcmds];
if (names)
theNames[headerIndex]
= (char *)loadCmd
+ ((struct fvmlib_command *)loadCmd)
->fvmlib.name.offset;
headerIndex++;
}
}
headers[numHeaders] = NULL;
if (names)
theNames[numHeaders] = NULL;
return headers;
}
-(struct mach_header **)getMachHeaders
{
return [self getMachHeadersWithNames: NULL];
}
-(unsigned)getSlide
{
return 0;
}
-(struct mach_header*)copyLoadedImage:(struct mach_header*)header_addr
withSlide:(unsigned)slide
{
struct load_command *cmd;
unsigned long fileSize = 0;
struct mach_header *header, *newHeader;
struct symtab_command *symtabCmd = 0;
kern_return_t r;
int i;
unsigned header_size;
header = [self pointerFor:header_addr
withSize:sizeof (struct mach_header)];
header_size = header->sizeofcmds + sizeof (struct mach_header);
header = [self pointerFor:header_addr
withSize:header_size];
// get the size of the original file
for (cmd = (struct load_command *)(header + 1), i = 0;
i < header->ncmds;
((void *)cmd) += cmd->cmdsize, i++)
{
unsigned long topOff;
if (cmd->cmd == LC_SEGMENT)
{
struct segment_command *segCmd = (struct segment_command *)cmd;
topOff = segCmd->fileoff + segCmd->filesize;
if (topOff > fileSize)
fileSize = topOff;
}
else if (cmd->cmd == LC_SYMTAB)
{
symtabCmd = (struct symtab_command*)cmd;
}
}
r = vm_allocate (task_self (), (vm_address_t*)&newHeader, fileSize, 1);
if (r != KERN_SUCCESS) [self error:"vm_allocate failed %d", r];
// get the size of the original file
for (cmd = (struct load_command *)(header + 1), i = 0;
i < header->ncmds;
((void *)cmd) += cmd->cmdsize, i++)
{
if (cmd->cmd == LC_SEGMENT)
{
struct segment_command *segCmd = (struct segment_command *)cmd;
void *segment = [self pointerFor:(char*)segCmd->vmaddr + slide
withSize:segCmd->filesize];
// get size rounded up to page size...
vm_size_t size = segCmd->filesize;
if (size % vm_page_size != 0)
{
size += vm_page_size - (size % vm_page_size);
}
if ((segCmd->fileoff % vm_page_size) == 0)
{
vm_copy (task_self (),
(vm_address_t)segment,
(vm_size_t)size,
(vm_address_t)((void*)newHeader + segCmd->fileoff));
}
else
{
memcpy (segment, (void*)newHeader + segCmd->fileoff, size);
}
}
}
// slide the image by modifying the load commands
for (cmd = (struct load_command *)(newHeader + 1), i = 0;
i < header->ncmds;
((void *)cmd) += cmd->cmdsize, i++)
{
if (cmd->cmd == LC_SEGMENT)
{
struct segment_command *segCmd = (struct segment_command *)cmd;
{
int n;
struct section *sect;
segCmd->vmaddr += slide;
for (sect = (struct section*) (segCmd + 1), n = 0;
n < segCmd->nsects;
sect += 1, n += 1)
{
sect->addr += slide;
}
}
}
}
// now relocate the symbol table information
{
struct nlist* symtab = (struct nlist*) ((char*)newHeader
+ symtabCmd->symoff);
for (i = 0; i < symtabCmd->nsyms; i++)
{
if (symtab[i].n_sect != NO_SECT)
{
symtab[i].n_value += slide;
}
}
}
return newHeader;
}
@end