#if defined(m68k) || defined(__i386__)
#import <stdlib.h>
#import <limits.h>
#import <mach/mach.h>
#import "stuff/openstep_mach.h"
#import <mach-o/nlist.h>
#import <mach-o/reloc.h>
#import "stuff/vm_flush_cache.h"
#import "stuff/bool.h"
#import "images.h"
#import "symbols.h"
#import "errors.h"
#import "reloc.h"
void
local_relocation(
struct image *image)
{
unsigned long i, r_slide, r_address, r_pcrel, r_length, r_type, r_value,
value;
enum bool no_sect;
struct relocation_info *relocs;
struct scattered_relocation_info *sreloc;
unsigned long cache_flush_high_addr, cache_flush_low_addr;
relocs = (struct relocation_info *)
(image->vmaddr_slide +
image->linkedit_segment->vmaddr +
image->dyst->locreloff -
image->linkedit_segment->fileoff);
cache_flush_high_addr = 0;
cache_flush_low_addr = ULONG_MAX;
if(image->mh->flags & MH_SPLIT_SEGS)
r_slide = image->segs_read_write_addr + image->vmaddr_slide;
else
r_slide = image->seg1addr + image->vmaddr_slide;
r_value = 0;
for(i = 0; i < image->dyst->nlocrel; i++){
if((relocs[i].r_address & R_SCATTERED) != 0){
sreloc = (struct scattered_relocation_info *)(relocs + i);
r_address = sreloc->r_address;
r_pcrel = sreloc->r_pcrel;
r_length = sreloc->r_length;
r_type = sreloc->r_type;
r_value = sreloc->r_value;
no_sect = FALSE;
}
else{
r_address = relocs[i].r_address;
r_pcrel = relocs[i].r_pcrel;
r_length = relocs[i].r_length;
r_type = relocs[i].r_type;
no_sect = relocs[i].r_symbolnum == NO_SECT;
}
value = 0;
if(r_pcrel)
value -= image->vmaddr_slide;
if(no_sect == FALSE)
value += image->vmaddr_slide;
if(r_type == GENERIC_RELOC_PB_LA_PTR){
value = r_value + image->vmaddr_slide;
*((long *)(r_address + r_slide)) = value;
}
else{
switch(r_length){
case 0:
value += *((char *)(r_address + r_slide));
if( (value & 0xffffff00) &&
((value & 0xffffff80) != 0xffffff80)){
error("relocation overflow (local relocation in %s "
"relocation entry %lu does not fit in 1 byte)",
image->name, i);
link_edit_error(DYLD_OTHER_ERROR, DYLD_RELOCATION,
image->name);
}
*((char *)(r_address + r_slide)) = value;
break;
case 1:
value += *((short *)(r_address + r_slide));
if( (value & 0xffff0000) &&
((value & 0xffff8000) != 0xffff8000)){
error("relocation overflow (local relocation in %s "
"relocation entry %lu does not fit in 2 bytes)",
image->name, i);
link_edit_error(DYLD_OTHER_ERROR, DYLD_RELOCATION,
image->name);
}
*((short *)(r_address + r_slide)) = value;
break;
case 2:
value += *((long *)(r_address + r_slide));
*((long *)(r_address + r_slide)) = value;
break;
}
}
if(image->cache_sync_on_reloc){
if(r_address + r_slide < cache_flush_low_addr)
cache_flush_low_addr = r_address + r_slide;
if(r_address + r_slide + (1 << r_length) >
cache_flush_high_addr)
cache_flush_high_addr = r_address + r_slide +
(1 << r_length);
}
}
if(image->cache_sync_on_reloc &&
cache_flush_high_addr > cache_flush_low_addr)
vm_flush_cache(mach_task_self(), cache_flush_low_addr,
cache_flush_high_addr - cache_flush_low_addr);
}
enum link_state
external_relocation(
struct image *image,
struct relocation_info *relocs,
unsigned long nrelocs,
struct nlist *symbols,
char *strings,
char *library_name,
char *module_name)
{
unsigned long i, value, r_slide;
enum link_state link_state;
char *symbol_name;
struct nlist *defined_symbol;
module_state *defined_module;
struct image *defined_image;
struct library_image *defined_library_image;
unsigned long cache_flush_high_addr, cache_flush_low_addr;
link_state = RELOCATED;
cache_flush_high_addr = 0;
cache_flush_low_addr = ULONG_MAX;
if(image->mh->flags & MH_SPLIT_SEGS)
r_slide = image->segs_read_write_addr + image->vmaddr_slide;
else
r_slide = image->seg1addr + image->vmaddr_slide;
for(i = 0; i < nrelocs; i++){
symbol_name = strings + symbols[relocs[i].r_symbolnum].n_un.n_strx;
if((image->private == TRUE &&
image->has_coalesced_sections == TRUE &&
is_symbol_coalesced(image,
symbols + relocs[i].r_symbolnum) == TRUE) ||
((symbols[relocs[i].r_symbolnum].n_type & N_EXT) != N_EXT &&
(symbols[relocs[i].r_symbolnum].n_type & N_PEXT) == N_PEXT) ){
defined_symbol = symbols + relocs[i].r_symbolnum;
defined_module = NULL;
defined_image = image;
defined_library_image = NULL;
}
else{
lookup_symbol(symbol_name,
get_primary_image(image, symbols +
relocs[i].r_symbolnum),
get_hint(image, symbols + relocs[i].r_symbolnum),
get_weak(symbols + relocs[i].r_symbolnum),
&defined_symbol, &defined_module,
&defined_image, &defined_library_image, NULL);
}
if(defined_symbol == NULL){
undo_external_relocation(
FALSE,
image,
relocs,
i,
symbols,
strings,
library_name,
module_name);
link_state = BEING_LINKED;
goto done;
}
value = defined_symbol->n_value;
if((defined_symbol->n_type & N_TYPE) != N_ABS)
value += defined_image->vmaddr_slide;
if(relocs[i].r_pcrel)
value -= image->vmaddr_slide;
switch(relocs[i].r_length){
case 0:
value += *((char *)(relocs[i].r_address + r_slide));
if( (value & 0xffffff00) &&
((value & 0xffffff80) != 0xffffff80)){
set_error_string("dyld: relocation overflow (external "
"relocation for symbol %s in ", symbol_name);
if(library_name != NULL)
add_error_string("%s(%s) ", library_name, module_name);
else
add_error_string("%s ", module_name);
add_error_string("relocation entry %lu does not fit in 1 "
"byte", i);
link_edit_error(DYLD_OTHER_ERROR, DYLD_RELOCATION,
image->name);
}
*((char *)(relocs[i].r_address + r_slide)) = value;
break;
case 1:
value += *((short *)(relocs[i].r_address + r_slide));
if( (value & 0xffff0000) &&
((value & 0xffff8000) != 0xffff8000)){
set_error_string("dyld: relocation overflow (external "
"relocation for symbol %s in ", symbol_name);
if(library_name != NULL)
add_error_string("%s(%s) ", library_name, module_name);
else
add_error_string("%s ", module_name);
add_error_string("relocation entry %lu does not fit in 2 "
"bytes", i);
link_edit_error(DYLD_OTHER_ERROR, DYLD_RELOCATION,
image->name);
}
*((short *)(relocs[i].r_address + r_slide)) = value;
break;
case 2:
value += *((long *)(relocs[i].r_address + r_slide));
*((long *)(relocs[i].r_address + r_slide)) = value;
break;
}
if(image->cache_sync_on_reloc){
if(relocs[i].r_address + r_slide < cache_flush_low_addr)
cache_flush_low_addr = relocs[i].r_address + r_slide;
if(relocs[i].r_address + r_slide + (1 << relocs[i].r_length) >
cache_flush_high_addr)
cache_flush_high_addr = relocs[i].r_address + r_slide +
(1 << relocs[i].r_length);
}
}
done:
if(image->cache_sync_on_reloc &&
cache_flush_high_addr > cache_flush_low_addr)
vm_flush_cache(mach_task_self(), cache_flush_low_addr,
cache_flush_high_addr - cache_flush_low_addr);
return(link_state);
}
void
undo_external_relocation(
enum bool undo_prebinding,
struct image *image,
struct relocation_info *relocs,
unsigned long nrelocs,
struct nlist *symbols,
char *strings,
char *library_name,
char *module_name)
{
unsigned long i, value, r_slide, contents;
char *symbol_name;
struct nlist *defined_symbol;
module_state *defined_module;
struct image *defined_image;
struct library_image *defined_library_image;
unsigned long cache_flush_high_addr, cache_flush_low_addr;
cache_flush_high_addr = 0;
cache_flush_low_addr = ULONG_MAX;
if(image->mh->flags & MH_SPLIT_SEGS)
r_slide = image->segs_read_write_addr + image->vmaddr_slide;
else
r_slide = image->seg1addr + image->vmaddr_slide;
for(i = 0; i < nrelocs; i++){
symbol_name = strings + symbols[relocs[i].r_symbolnum].n_un.n_strx;
if(undo_prebinding == TRUE){
value = symbols[relocs[i].r_symbolnum].n_value;
}
else{
if(image->private == TRUE &&
image->has_coalesced_sections == TRUE &&
is_symbol_coalesced(image,
symbols+relocs[i].r_symbolnum) == TRUE){
defined_symbol = symbols + relocs[i].r_symbolnum;
defined_module = NULL;
defined_image = image;
defined_library_image = NULL;
}
else{
lookup_symbol(symbol_name,
get_primary_image(image, symbols +
relocs[i].r_symbolnum),
get_hint(image, symbols +
relocs[i].r_symbolnum),
get_weak(symbols + relocs[i].r_symbolnum),
&defined_symbol, &defined_module,
&defined_image, &defined_library_image, NULL);
}
value = defined_symbol->n_value;
if((defined_symbol->n_type & N_TYPE) != N_ABS)
value += defined_image->vmaddr_slide;
if(relocs[i].r_pcrel)
value -= image->vmaddr_slide;
}
switch(relocs[i].r_length){
case 0:
contents = *((char *)(relocs[i].r_address + r_slide));
contents -= value;
if( (contents & 0xffffff00) &&
((contents & 0xffffff80) != 0xffffff80)){
set_error_string("dyld: relocation overflow (external "
"relocation for symbol %s in ", symbol_name);
if(library_name != NULL)
add_error_string("%s(%s) ", library_name, module_name);
else
add_error_string("%s ", module_name);
add_error_string("relocation entry %lu does not fit in 1 "
"byte", i);
link_edit_error(DYLD_OTHER_ERROR, DYLD_RELOCATION,
image->name);
}
*((char *)(relocs[i].r_address + r_slide)) = contents;
break;
case 1:
contents = *((short *)(relocs[i].r_address + r_slide));
contents -= value;
if( (contents & 0xffff0000) &&
((contents & 0xffff8000) != 0xffff8000)){
set_error_string("dyld: relocation overflow (external "
"relocation for symbol %s in ", symbol_name);
if(library_name != NULL)
add_error_string("%s(%s) ", library_name, module_name);
else
add_error_string("%s ", module_name);
add_error_string("relocation entry %lu does not fit in 2 "
"bytes", i);
link_edit_error(DYLD_OTHER_ERROR, DYLD_RELOCATION,
image->name);
}
*((short *)(relocs[i].r_address + r_slide)) = contents;
break;
case 2:
contents = *((long *)(relocs[i].r_address + r_slide));
contents -= value;
*((long *)(relocs[i].r_address + r_slide)) = contents;
break;
}
if(image->cache_sync_on_reloc){
if(relocs[i].r_address + r_slide < cache_flush_low_addr)
cache_flush_low_addr = relocs[i].r_address + r_slide;
if(relocs[i].r_address + r_slide + (1 << relocs[i].r_length) >
cache_flush_high_addr)
cache_flush_high_addr = relocs[i].r_address + r_slide +
(1 << relocs[i].r_length);
}
}
if(image->cache_sync_on_reloc &&
cache_flush_high_addr > cache_flush_low_addr)
vm_flush_cache(mach_task_self(), cache_flush_low_addr,
cache_flush_high_addr - cache_flush_low_addr);
}
#endif