#ifndef RLD
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <mach/mach.h>
#include <mach-o/loader.h>
#include <mach-o/reloc.h>
#include <mach-o/i860/reloc.h>
#include <mach-o/nlist.h>
#include "stuff/bool.h"
#include "stuff/bytesex.h"
#include "ld.h"
#include "live_refs.h"
#include "objects.h"
#include "sections.h"
#include "pass1.h"
#include "symbols.h"
#include "pass2.h"
#include "generic_reloc.h"
#include "i860_reloc.h"
#include "dylibs.h"
__private_extern__
void
i860_reloc(
char *contents,
struct relocation_info *relocs,
struct section_map *section_map)
{
unsigned long i, j, symbolnum, value;
unsigned long instruction, immediate;
struct nlist *nlists;
char *strings;
enum bool force_extern_reloc;
struct undefined_map *undefined_map;
struct merged_symbol *merged_symbol;
struct section_map *local_map, *pair_local_map;
struct relocation_info *reloc, *pair_reloc;
struct scattered_relocation_info *sreloc, *spair_reloc;
unsigned long r_address, r_symbolnum, r_pcrel, r_length, r_extern,
r_scattered, r_value, pair_r_symbolnum, pair_r_value;
enum reloc_type_i860 r_type, pair_r_type;
unsigned short other_half;
unsigned long offset;
#ifdef DEBUG
merged_symbol = NULL;
local_map = NULL;
immediate = 0;
pair_local_map = NULL;
pair_r_symbolnum = 0;
pair_r_value = 0;
other_half = 0;
offset = 0;
#endif
for(i = 0; i < section_map->s->nreloc; i++){
force_extern_reloc = FALSE;
if((relocs[i].r_address & R_SCATTERED) != 0){
sreloc = (struct scattered_relocation_info *)(relocs + i);
reloc = NULL;
r_scattered = 1;
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_i860)sreloc->r_type;
r_extern = 0;
if(r_type == I860_RELOC_PAIR){
error_with_cur_obj("stray relocation I860_RELOC_PAIR entry "
"(%lu) in section (%.16s,%.16s)", i,
section_map->s->segname, section_map->s->sectname);
continue;
}
r_symbolnum = 0;
for(j = 0; j < cur_obj->nsection_maps; j++){
if(r_value >= cur_obj->section_maps[j].s->addr &&
r_value < cur_obj->section_maps[j].s->addr +
cur_obj->section_maps[j].s->size){
r_symbolnum = j + 1;
break;
}
}
if(r_symbolnum == 0){
for(j = 0; j < cur_obj->nsection_maps; j++){
if(r_value == cur_obj->section_maps[j].s->addr +
cur_obj->section_maps[j].s->size){
r_symbolnum = j + 1;
break;
}
}
if(r_symbolnum == 0){
error_with_cur_obj("r_value (0x%x) field of relocation "
"entry %lu in section (%.16s,%.16s) out of range",
(unsigned int)r_value, i, section_map->s->segname,
section_map->s->sectname);
return;
}
}
}
else{
reloc = relocs + i;
sreloc = NULL;
r_scattered = 0;
r_address = reloc->r_address;
r_pcrel = reloc->r_pcrel;
r_length = reloc->r_length;
r_extern = reloc->r_extern;
r_symbolnum = reloc->r_symbolnum;
r_type = (enum reloc_type_i860)reloc->r_type;
r_value = 0;
}
if(r_type == I860_RELOC_PAIR){
error_with_cur_obj("stray relocation I860_RELOC_PAIR entry "
"(%lu) in section (%.16s,%.16s)", i,
section_map->s->segname, section_map->s->sectname);
continue;
}
if(r_address >= section_map->s->size){
error_with_cur_obj("r_address (0x%x) field of relocation entry "
"%lu in section (%.16s,%.16s) out of range",
(unsigned int)r_address, i, section_map->s->segname,
section_map->s->sectname);
return;
}
pair_r_type = (enum reloc_type_i860)0;
pair_reloc = NULL;
spair_reloc = NULL;
if(r_type == I860_RELOC_HIGH || r_type == I860_RELOC_HIGHADJ){
if(i + 1 < section_map->s->nreloc){
pair_reloc = relocs + i + 1;
if((pair_reloc->r_address & R_SCATTERED) != 0){
spair_reloc = (struct scattered_relocation_info *)
pair_reloc;
pair_reloc = NULL;
pair_r_type = (enum reloc_type_i860)spair_reloc->r_type;
other_half = spair_reloc->r_address & 0xffff;
}
else{
pair_r_type = (enum reloc_type_i860)pair_reloc->r_type;
other_half = pair_reloc->r_address & 0xffff;
}
}
if((pair_reloc == NULL && spair_reloc == NULL) ||
pair_r_type != I860_RELOC_PAIR){
error_with_cur_obj("relocation entry (%lu) in section "
"(%.16s,%.16s) missing following associated "
"I860_RELOC_PAIR entry", i, section_map->s->segname,
section_map->s->sectname);
continue;
}
}
else if(r_type == I860_RELOC_SECTDIFF){
if(r_scattered != 1){
error_with_cur_obj("relocation entry (%lu) in section "
"(%.16s,%.16s) r_type is I860_RELOC_SECTDIFF but "
"relocation entry not scattered type", i,
section_map->s->segname, section_map->s->sectname);
continue;
}
if(i + 1 < section_map->s->nreloc){
pair_reloc = relocs + i + 1;
if((pair_reloc->r_address & R_SCATTERED) != 0){
spair_reloc = (struct scattered_relocation_info *)
pair_reloc;
pair_reloc = NULL;
pair_r_type = (enum reloc_type_i860)spair_reloc->r_type;
pair_r_value = spair_reloc->r_value;
}
else{
error_with_cur_obj("relocation entry (%lu) in section "
"(%.16s,%.16s) following associated relocation "
"entry not scattered type", i,
section_map->s->segname, section_map->s->sectname);
continue;
}
}
if((pair_reloc == NULL && spair_reloc == NULL) ||
pair_r_type != I860_RELOC_PAIR){
error_with_cur_obj("relocation entry (%lu) in section "
"(%.16s,%.16s) missing following associated "
"I860_RELOC_PAIR entry", i, section_map->s->segname,
section_map->s->sectname);
continue;
}
pair_r_symbolnum = 0;
for(j = 0; j < cur_obj->nsection_maps; j++){
if(pair_r_value >= cur_obj->section_maps[j].s->addr &&
pair_r_value < cur_obj->section_maps[j].s->addr +
cur_obj->section_maps[j].s->size){
pair_r_symbolnum = j + 1;
break;
}
}
if(pair_r_symbolnum == 0){
error_with_cur_obj("r_value (0x%x) field of relocation "
"entry %lu in section (%.16s,%.16s) out of range",
(unsigned int)r_value, i + 1, section_map->s->segname,
section_map->s->sectname);
return;
}
}
if(r_extern){
if(r_symbolnum >= cur_obj->symtab->nsyms){
error_with_cur_obj("r_symbolnum (%lu) field of external "
"relocation entry %lu in section (%.16s,%.16s) out of "
"range", r_symbolnum, i, section_map->s->segname,
section_map->s->sectname);
return;
}
symbolnum = r_symbolnum;
undefined_map = bsearch(&symbolnum, cur_obj->undefined_maps,
cur_obj->nundefineds, sizeof(struct undefined_map),
(int (*)(const void *, const void *))undef_bsearch);
if(undefined_map != NULL){
merged_symbol = undefined_map->merged_symbol;
}
else{
nlists = (struct nlist *)(cur_obj->obj_addr +
cur_obj->symtab->symoff);
strings = (char *)(cur_obj->obj_addr +
cur_obj->symtab->stroff);
if((nlists[symbolnum].n_type & N_EXT) != N_EXT){
error_with_cur_obj("r_symbolnum (%lu) field of external"
" relocation entry %lu in section (%.16s,%.16s) "
"refers to a non-external symbol", symbolnum, i,
section_map->s->segname, section_map->s->sectname);
return;
}
if((nlists[symbolnum].n_type & N_TYPE) == N_SECT &&
(cur_obj->section_maps[nlists[symbolnum].n_sect-1].
s->flags & SECTION_TYPE) == S_COALESCED){
merged_symbol = lookup_symbol(strings +
nlists[symbolnum].n_un.n_strx);
if(merged_symbol->name_len == 0){
fatal("internal error, in i860_reloc() failed to "
"lookup coalesced symbol %s", strings +
nlists[symbolnum].n_un.n_strx);
}
}
else{
if((nlists[symbolnum].n_type & N_EXT) != N_EXT ||
(nlists[symbolnum].n_type & N_TYPE) != N_UNDF){
error_with_cur_obj("r_symbolnum (%lu) field of "
"external relocation entry %lu in section "
"(%.16s,%.16s) refers to a non-undefined "
"symbol", symbolnum, i, section_map->s->segname,
section_map->s->sectname);
return;
}
print_obj_name(cur_obj);
fatal("internal error, in i860_reloc() symbol index %lu"
" in above file not in undefined map", symbolnum);
}
}
if((merged_symbol->nlist.n_type & N_TYPE) == N_SECT &&
(get_output_section(merged_symbol->nlist.n_sect)->
flags & SECTION_TYPE) == S_COALESCED){
if(((merged_symbol->nlist.n_type & N_PEXT) == N_PEXT &&
keep_private_externs == FALSE) ||
dynamic == FALSE ||
(output_for_dyld && has_dynamic_linker_command))
force_extern_reloc = FALSE;
else
force_extern_reloc = TRUE;
}
if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR)
merged_symbol = (struct merged_symbol *)
merged_symbol->nlist.n_value;
if(((merged_symbol->nlist.n_type & N_TYPE) == N_UNDF) ||
(force_extern_reloc == TRUE && prebinding == FALSE) ||
((filetype == MH_DYLIB && multi_module_dylib == TRUE) &&
(((merged_symbol->nlist.n_type & N_PEXT) != N_PEXT) &&
prebinding == FALSE) ) )
value = 0;
else{
value = merged_symbol->nlist.n_value;
if(r_type == I860_RELOC_VANILLA){
switch(r_length){
case 0:
offset = get_byte((char *)(contents + r_address));
break;
case 1:
offset = get_short((short *)(contents + r_address));
break;
case 2:
offset = get_long((long *)(contents + r_address));
break;
default:
break;
}
if(r_pcrel)
offset += section_map->s->addr + r_address;
}
else{
instruction = get_long((long *)(contents + r_address));
switch(r_type){
case I860_RELOC_HIGH:
offset = ((instruction & 0xffff) << 16) |
other_half;
break;
case I860_RELOC_HIGHADJ:
if(other_half & 0x8000)
offset = ((instruction & 0xffff) << 16) +
(other_half | 0xffff0000);
else
offset = ((instruction & 0xffff) << 16) +
other_half;
break;
case I860_RELOC_LOW0:
offset = instruction & 0xffff;
if((offset & 0x8000) != 0)
offset |= 0xffff0000;
break;
case I860_RELOC_LOW1:
offset = instruction & 0xfffe;
if((offset & 0x8000) != 0)
offset |= 0xffff0000;
break;
case I860_RELOC_LOW2:
offset = instruction & 0xfffc;
if((offset & 0x8000) != 0)
offset |= 0xffff0000;
break;
case I860_RELOC_LOW3:
offset = instruction & 0xfff8;
if((offset & 0x8000) != 0)
offset |= 0xffff0000;
break;
case I860_RELOC_LOW4:
offset = instruction &0xfff0;
if((offset & 0x8000) != 0)
offset |= 0xffff0000;
break;
case I860_RELOC_SPLIT0:
offset = ((instruction >> 5) & 0xf800) |
(instruction & 0x7ff);
if((offset & 0x8000) != 0)
offset |= 0xffff0000;
if(r_pcrel)
offset <<= 2;
break;
case I860_RELOC_SPLIT1:
offset = ((instruction >> 5) & 0xf800) |
(instruction & 0x7fe);
if((offset & 0x8000) != 0)
offset |= 0xffff0000;
break;
case I860_RELOC_SPLIT2:
offset = ((instruction >> 5) & 0xf800) |
(instruction & 0x7fc);
if((offset & 0x8000) != 0)
offset |= 0xffff0000;
break;
case I860_RELOC_BRADDR:
offset = instruction & 0x03ffffff;
if((offset & 0x02000000) != 0)
offset |= 0xfc000000;
offset <<= 2;
break;
default:
break;
}
}
}
if((merged_symbol->nlist.n_type & N_TYPE) == N_SECT)
output_sections[merged_symbol->nlist.n_sect]->referenced =
TRUE;
}
else{
value = 0;
if(r_symbolnum != R_ABS){
if(r_symbolnum > cur_obj->nsection_maps){
error_with_cur_obj("r_symbolnum (%lu) field of local "
"relocation entry %lu in section (%.16s,%.16s) "
"out of range", r_symbolnum, i,
section_map->s->segname, section_map->s->sectname);
return;
}
local_map = &(cur_obj->section_maps[r_symbolnum - 1]);
local_map->output_section->referenced = TRUE;
pair_local_map = NULL;
if(r_type == I860_RELOC_SECTDIFF){
pair_local_map =
&(cur_obj->section_maps[pair_r_symbolnum - 1]);
pair_local_map->output_section->referenced = TRUE;
}
if(local_map->nfine_relocs == 0 &&
(pair_local_map == NULL ||
pair_local_map->nfine_relocs == 0) ){
if(r_type == I860_RELOC_SECTDIFF){
value = - local_map->s->addr
+ (local_map->output_section->s.addr +
local_map->offset)
+ pair_local_map->s->addr
- (pair_local_map->output_section->s.addr +
pair_local_map->offset);
}
else{
value = - local_map->s->addr
+ (local_map->output_section->s.addr +
local_map->offset);
}
}
else{
fatal("internal error, in i860_reloc() section (%.16s,"
"%.16s) has fine relocs", local_map->s->segname,
local_map->s->sectname);
}
}
}
if(r_pcrel){
if(section_map->nfine_relocs == 0)
value += + section_map->s->addr
- (section_map->output_section->s.addr +
section_map->offset );
else{
fatal("internal error, in i860_reloc() section (%.16s,"
"%.16s) has fine relocs", local_map->s->segname,
local_map->s->sectname);
}
}
if(r_type == I860_RELOC_VANILLA ||
r_type == I860_RELOC_SECTDIFF){
switch(r_length){
case 0:
value += get_byte((char *)(contents + r_address));
if( (value & 0xffffff00) &&
((value & 0xffffff80) != 0xffffff80))
error_with_cur_obj("relocation for entry %lu in section"
" (%.16s,%.16s) does not fit in 1 byte", i,
section_map->s->segname, section_map->s->sectname);
set_byte((char *)(contents + r_address), value);
break;
case 1:
value += get_short((short *)(contents + r_address));
if( (value & 0xffff0000) &&
((value & 0xffff8000) != 0xffff8000))
error_with_cur_obj("relocation for entry %lu in section"
" (%.16s,%.16s) does not fit in 2 bytes", i,
section_map->s->segname, section_map->s->sectname);
set_short((short *)(contents + r_address), value);
break;
case 2:
value += get_long((long *)(contents + r_address));
set_long((long *)(contents + r_address), value);
break;
default:
error_with_cur_obj("r_length field of relocation entry %lu "
"in section (%.16s,%.16s) invalid", i,
section_map->s->segname, section_map->s->sectname);
return;
}
}
else{
instruction = get_long((long *)(contents + r_address));
switch(r_type){
case I860_RELOC_HIGH:
immediate = ((instruction & 0xffff) << 16) | other_half;
immediate += value;
instruction = (instruction & 0xffff0000) |
((immediate >> 16) & 0xffff);
other_half = immediate & 0xffff;
break;
case I860_RELOC_HIGHADJ:
if(other_half & 0x8000)
immediate = ((instruction & 0xffff) << 16) +
(other_half | 0xffff0000);
else
immediate = ((instruction & 0xffff) << 16) + other_half;
immediate += value;
if(immediate & 0x8000)
instruction = (instruction & 0xffff0000) |
(((immediate >> 16) + 1) & 0xffff);
else
instruction = (instruction & 0xffff0000) |
((immediate >> 16) & 0xffff);
other_half = immediate & 0xffff;
break;
case I860_RELOC_LOW0:
immediate = instruction & 0xffff;
if((immediate & 0x8000) != 0)
immediate |= 0xffff0000;
immediate += value;
instruction = (instruction & 0xffff0000) |
(immediate & 0xffff);
break;
case I860_RELOC_LOW1:
immediate = instruction & 0xfffe;
if((immediate & 0x8000) != 0)
immediate |= 0xffff0000;
immediate += value;
instruction = (instruction & 0xffff0001) |
(immediate & 0xfffe);
break;
case I860_RELOC_LOW2:
immediate = instruction & 0xfffc;
if((immediate & 0x8000) != 0)
immediate |= 0xffff0000;
immediate += value;
instruction = (instruction & 0xffff0003) |
(immediate & 0xfffc);
break;
case I860_RELOC_LOW3:
immediate = instruction & 0xfff8;
if((immediate & 0x8000) != 0)
immediate |= 0xffff0000;
immediate += value;
instruction = (instruction & 0xffff0007) |
(immediate & 0xfff8);
break;
case I860_RELOC_LOW4:
immediate = instruction &0xfff0;
if((immediate & 0x8000) != 0)
immediate |= 0xffff0000;
immediate += value;
instruction = (instruction & 0xffff000f) |
(immediate & 0xfff0);
break;
case I860_RELOC_SPLIT0:
immediate = ((instruction >> 5) & 0xf800) |
(instruction & 0x7ff);
if((immediate & 0x8000) != 0)
immediate |= 0xffff0000;
if(r_pcrel)
immediate <<= 2;
immediate += value;
if(r_pcrel)
immediate >>= 2;
instruction = (instruction & 0xffe0f800) |
((immediate & 0xf800) << 5) |
(immediate & 0x7ff);
break;
case I860_RELOC_SPLIT1:
immediate = ((instruction >> 5) & 0xf800) |
(instruction & 0x7fe);
if((immediate & 0x8000) != 0)
immediate |= 0xffff0000;
immediate += value;
instruction = (instruction & 0xffe0f801) |
((immediate & 0xf800) << 5) |
(immediate & 0x7fe);
break;
case I860_RELOC_SPLIT2:
immediate = ((instruction >> 5) & 0xf800) |
(instruction & 0x7fc);
if((immediate & 0x8000) != 0)
immediate |= 0xffff0000;
immediate += value;
instruction = (instruction & 0xffe0f803) |
((immediate & 0xf800) << 5) |
(immediate & 0x7FC);
break;
case I860_RELOC_BRADDR:
immediate = instruction & 0x03ffffff;
if((immediate & 0x02000000) != 0)
immediate |= 0xfc000000;
immediate <<= 2;
immediate += value;
immediate >>= 2;
instruction = (instruction & 0xfc000000) |
(immediate & 0x03ffffff);
break;
case I860_RELOC_PAIR:
default:
error_with_cur_obj("r_type field of relocation entry %lu "
"in section (%.16s,%.16s) invalid", i,
section_map->s->segname, section_map->s->sectname);
continue;
}
set_long((long *)(contents + r_address), instruction);
}
if(save_reloc || output_for_dyld){
if(r_extern){
if((merged_symbol->nlist.n_type & N_TYPE) != N_UNDF &&
(merged_symbol->nlist.n_type & N_TYPE) != N_PBUD &&
force_extern_reloc == FALSE &&
((filetype != MH_DYLIB ||
multi_module_dylib == FALSE) ||
(merged_symbol->nlist.n_type & N_PEXT) == N_PEXT)){
reloc->r_extern = 0;
reloc->r_extern = 0;
if(merged_symbol->definition_object == base_obj ||
(merged_symbol->nlist.n_type & N_TYPE) == N_ABS){
reloc->r_symbolnum = R_ABS;
}
else{
if(offset == 0){
reloc->r_symbolnum =merged_symbol->nlist.n_sect;
}
else{
sreloc = (struct scattered_relocation_info *)
reloc;
r_scattered = 1;
sreloc->r_scattered = r_scattered;
sreloc->r_address = r_address;
sreloc->r_pcrel = r_pcrel;
sreloc->r_length = r_length;
sreloc->r_type = r_type;
sreloc->r_value = merged_symbol->nlist.n_value;
}
}
}
else{
reloc->r_symbolnum =
merged_symbol_output_index(merged_symbol);
}
}
else if(r_scattered == 0){
if(reloc->r_symbolnum != R_ABS)
reloc->r_symbolnum =
local_map->output_section->output_sectnum;
}
else{
sreloc->r_value += - local_map->s->addr
+ (local_map->output_section->s.addr +
local_map->offset);
}
if(r_scattered == 0){
reloc->r_address += section_map->offset;
}
else{
sreloc->r_address += section_map->offset;
}
if(pair_r_type == I860_RELOC_PAIR){
if(pair_reloc != NULL)
pair_reloc->r_address = other_half;
else if(spair_reloc != NULL){
if(r_type == I860_RELOC_SECTDIFF){
spair_reloc->r_value +=
- pair_local_map->s->addr
+ (pair_local_map->output_section->s.addr +
pair_local_map->offset);
}
else{
spair_reloc->r_address = other_half;
}
}
else{
fatal("internal error, in i860_reloc() pair_r_type "
"is I860_RELOC_PAIR but pair_reloc and spair_reloc "
"are NULL");
}
}
}
if(pair_r_type == I860_RELOC_PAIR)
i++;
}
}
#endif