#ifdef sparc
extern void sparc_cache_flush(
unsigned long double_word_address);
#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 <mach-o/sparc/reloc.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_value, value;
unsigned long instruction, immediate;
enum bool no_sect;
struct relocation_info *relocs;
struct scattered_relocation_info *sreloc;
struct relocation_info *pair_reloc;
struct scattered_relocation_info *spair_reloc;
enum reloc_type_sparc r_type, pair_r_type;
unsigned long other_half;
relocs = (struct relocation_info *)
(image->vmaddr_slide +
image->linkedit_segment->vmaddr +
image->dyst->locreloff -
image->linkedit_segment->fileoff);
other_half = 0;
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_value = sreloc->r_value;
r_type = (enum reloc_type_sparc)sreloc->r_type;
no_sect = FALSE;
}
else{
r_address = relocs[i].r_address;
r_pcrel = relocs[i].r_pcrel;
r_length = relocs[i].r_length;
r_type = (enum reloc_type_sparc)relocs[i].r_type;
no_sect = relocs[i].r_symbolnum == NO_SECT;
}
pair_r_type = 0;
if(r_type == SPARC_RELOC_HI22 ||
r_type == SPARC_RELOC_LO10 ){
pair_reloc = relocs + i + 1;
if((pair_reloc->r_address & R_SCATTERED) != 0){
spair_reloc = (struct scattered_relocation_info *)
pair_reloc;
pair_r_type = spair_reloc->r_type;
other_half = spair_reloc->r_address;
}
else{
pair_r_type = pair_reloc->r_type;
other_half = pair_reloc->r_address;
}
i++;
}
value = 0;
if(r_pcrel)
value -= image->vmaddr_slide;
if(no_sect == FALSE)
value += image->vmaddr_slide;
if(r_type == SPARC_RELOC_VANILLA){
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;
}
}
else {
switch(r_type) {
case SPARC_RELOC_PB_LA_PTR:
value = r_value + image->vmaddr_slide;
*((long *)(r_address + r_slide)) = value;
break;
case SPARC_RELOC_HI22:
instruction = *((long *)(r_address + r_slide));
immediate = ((instruction & 0x3fffff) << 10) | other_half;
immediate += value;
instruction = (instruction & 0xffc00000) |
((immediate >> 10) & 0x3fffff);
*((long *)(r_address + r_slide)) = instruction;
break;
case SPARC_RELOC_LO10:
instruction = *((long *)(r_address + r_slide));
immediate = (instruction & 0x3ff) | (other_half << 10);
immediate += value;
instruction = (instruction & 0xfffffc00) |
(immediate & 0x3ff);
*((long *)(r_address + r_slide)) = instruction;
break;
case SPARC_RELOC_WDISP22:
instruction = *((long *)(r_address + r_slide));
immediate = (instruction & 0x3fffff);
if ((immediate & 0x200000) != 0)
immediate |= 0xffc00000;
immediate <<= 2;
immediate += value;
if ((immediate & 0xff800000) != 0xff800000 &&
(immediate & 0xff800000) != 0x00) {
error("relocation overflow for relocation "
"entry %lu in %s (displacement too large)",
i, image->name);
link_edit_error(DYLD_OTHER_ERROR,
DYLD_RELOCATION, image->name);
}
immediate >>= 2;
instruction = (instruction & 0xffc00000) |
(immediate & 0x3fffff);
*((long *)(r_address + r_slide)) = instruction;
break;
case SPARC_RELOC_WDISP30:
instruction = *((long *)(r_address + r_slide));
immediate = (instruction & 0x3fffffff);
immediate <<= 2;
immediate += value;
immediate >>= 2;
instruction = (instruction & 0xc0000000) |
(immediate & 0x3fffffff);
*((long *)(r_address + r_slide)) = instruction;
break;
default:
break;
}
if(image->cache_sync_on_reloc)
sparc_cache_flush((r_address + r_slide) & ~0x3);
}
}
}
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 instruction, immediate;
unsigned long other_half;
link_state = RELOCATED;
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),
&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;
if(relocs[i].r_type == SPARC_RELOC_VANILLA){
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;
}
}
else{
instruction = *((long *)(relocs[i].r_address + r_slide));
switch(relocs[i].r_type){
case SPARC_RELOC_HI22:
other_half = (relocs[i + 1].r_address) & 0x3ff;
immediate = ((instruction & 0x3fffff) << 10) | other_half;
immediate += value;
instruction = (instruction & 0xffc00000) |
((immediate >> 10) & 0x3fffff);
break;
case SPARC_RELOC_LO10:
other_half = ((relocs[i + 1].r_address) >> 10) & 0x3fffff;
immediate = (instruction & 0x3ff) | (other_half << 10);
immediate += value;
instruction = (instruction & 0xfffffc00) |
(immediate & 0x3ff);
break;
case SPARC_RELOC_WDISP22:
immediate = (instruction & 0x3fffff);
if ((immediate & 0x200000) != 0)
immediate |= 0xffc00000;
immediate <<= 2;
immediate += value;
if ((immediate & 0xff800000) != 0xff800000 &&
(immediate & 0xff800000) != 0x00) {
error("relocation overflow for relocation "
"entry %lu in %s (displacement too large)",
i, image->name);
link_edit_error(DYLD_OTHER_ERROR,
DYLD_RELOCATION, image->name);
}
immediate >>= 2;
instruction = (instruction & 0xffc00000) |
(immediate & 0x3fffff);
break;
case SPARC_RELOC_WDISP30:
immediate = (instruction & 0x3fffffff);
immediate <<= 2;
immediate += value;
immediate >>= 2;
instruction = (instruction & 0xc0000000) |
(immediate & 0x3fffffff);
break;
default:
break;
}
*((long *)(relocs[i].r_address + r_slide)) = instruction;
if(image->cache_sync_on_reloc)
sparc_cache_flush((relocs[i].r_address + r_slide) & ~0x3);
}
if(relocs[i].r_type == SPARC_RELOC_LO10 ||
relocs[i].r_type == SPARC_RELOC_HI22)
i++;
}
done:
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 instruction, immediate;
unsigned long other_half;
enum bool relocs_writable;
kern_return_t r;
relocs_writable = FALSE;
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),
&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;
}
if(relocs[i].r_type == SPARC_RELOC_VANILLA){
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;
}
}
else{
if(relocs_writable == FALSE){
if((r = vm_protect(mach_task_self(),
image->linkedit_segment->vmaddr + image->vmaddr_slide,
(vm_size_t)image->linkedit_segment->vmsize, FALSE,
VM_PROT_WRITE | VM_PROT_READ)) != KERN_SUCCESS){
mach_error(r, "can't set vm_protection on segment: "
"%.16s for: %s", image->linkedit_segment->segname,
image->name);
link_edit_error(DYLD_MACH_RESOURCE, r, image->name);
}
relocs_writable = TRUE;
}
instruction = *((long *)(relocs[i].r_address + r_slide));
switch(relocs[i].r_type){
case SPARC_RELOC_HI22:
other_half = (relocs[i + 1].r_address) & 0x3ff;
immediate = ((instruction & 0x3fffff) << 10) | other_half;
immediate -= value;
instruction = (instruction & 0xffc00000) |
((immediate >> 10) & 0x3fffff);
relocs[i + 1].r_address = immediate & 0x3ff;
break;
case SPARC_RELOC_LO10:
other_half = ((relocs[i + 1].r_address) >> 10) & 0x3fffff;
immediate = (instruction & 0x3ff) | (other_half << 10);
immediate -= value;
instruction = (instruction & 0xfffffc00) |
(immediate & 0x3ff);
relocs[i + 1].r_address = (immediate >> 10) & 0x3fffff;
break;
case SPARC_RELOC_WDISP22:
immediate = (instruction & 0x3fffff);
if ((immediate & 0x200000) != 0)
immediate |= 0xffc00000;
immediate <<= 2;
immediate -= value;
if ((immediate & 0xff800000) != 0xff800000 &&
(immediate & 0xff800000) != 0x00) {
error("relocation overflow for relocation "
"entry %lu in %s (displacement too large)",
i, image->name);
link_edit_error(DYLD_OTHER_ERROR,
DYLD_RELOCATION, image->name);
}
immediate >>= 2;
instruction = (instruction & 0xffc00000) |
(immediate & 0x3fffff);
break;
case SPARC_RELOC_WDISP30:
immediate = (instruction & 0x3fffffff);
immediate <<= 2;
immediate -= value;
immediate >>= 2;
instruction = (instruction & 0xc0000000) |
(immediate & 0x3fffffff);
break;
default:
break;
}
*((long *)(relocs[i].r_address + r_slide)) = instruction;
if(image->cache_sync_on_reloc)
sparc_cache_flush((relocs[i].r_address + r_slide) & ~0x3);
}
if(relocs[i].r_type == SPARC_RELOC_LO10 ||
relocs[i].r_type == SPARC_RELOC_HI22)
i++;
}
if(relocs_writable == TRUE){
if((r = vm_protect(mach_task_self(),
image->linkedit_segment->vmaddr + image->vmaddr_slide,
(vm_size_t)image->linkedit_segment->vmsize,
FALSE, image->linkedit_segment->initprot)) != KERN_SUCCESS){
mach_error(r, "can't set vm_protection on segment: %.16s "
"for: %s", image->linkedit_segment->segname, image->name);
link_edit_error(DYLD_MACH_RESOURCE, r, image->name);
}
}
}
#endif