#ifdef SHLIB
#include "shlib.h"
#endif
#include <stdlib.h>
#if !(defined(KLD) && defined(__STATIC__))
#include <stdio.h>
#include <mach/mach.h>
#else
#include <mach/kern_return.h>
#endif
#include <string.h>
#include <stdarg.h>
#include <mach-o/loader.h>
#include <mach-o/reloc.h>
#include <mach-o/ppc/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 "ppc_reloc.h"
#include "indirect_sections.h"
#include "dylibs.h"
#define U_ABS(l) (((long)(l))<0 ? (unsigned long)(-(l)) : (l))
__private_extern__
void
ppc_reloc(
char *contents,
struct relocation_info *relocs,
struct section_map *section_map,
struct live_refs *refs,
unsigned long reloc_index)
{
unsigned long i, j, symbolnum, value, input_pc, output_pc;
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_ppc r_type, pair_r_type;
unsigned long other_half;
unsigned long offset;
unsigned long br14_disp_sign;
#if defined(DEBUG) || defined(RLD)
merged_symbol = NULL;
local_map = NULL;
instruction = 0;
other_half = 0;
immediate = 0;
offset = 0;
pair_r_symbolnum = 0;
pair_r_value = 0;
pair_local_map = NULL;
#endif
if(refs != NULL)
memset(refs, '\0', sizeof(struct live_refs));
else
reloc_index = 0;
for(i = reloc_index; i < section_map->s->nreloc; i++){
br14_disp_sign = 0;
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_ppc)sreloc->r_type;
r_extern = 0;
if(r_type == PPC_RELOC_PAIR){
error_with_cur_obj("stray relocation PPC_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_ppc)reloc->r_type;
r_value = 0;
}
if(r_type == PPC_RELOC_PAIR){
error_with_cur_obj("stray relocation PPC_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_ppc)0;
pair_reloc = NULL;
spair_reloc = NULL;
if(r_type == PPC_RELOC_HI16 || r_type == PPC_RELOC_LO16 ||
r_type == PPC_RELOC_HA16 || r_type == PPC_RELOC_LO14 ||
r_type == PPC_RELOC_JBSR){
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_ppc)spair_reloc->r_type;
if(r_type == PPC_RELOC_JBSR)
other_half = spair_reloc->r_value;
else
other_half = spair_reloc->r_address & 0xffff;
}
else{
pair_r_type = (enum reloc_type_ppc)pair_reloc->r_type;
if(r_type == PPC_RELOC_JBSR)
other_half = pair_reloc->r_address;
else
other_half = pair_reloc->r_address & 0xffff;
}
}
if((pair_reloc == NULL && spair_reloc == NULL) ||
pair_r_type != PPC_RELOC_PAIR){
error_with_cur_obj("relocation entry (%lu) in section "
"(%.16s,%.16s) missing following associated "
"PPC_RELOC_PAIR entry", i, section_map->s->segname,
section_map->s->sectname);
continue;
}
}
else if(r_type == PPC_RELOC_SECTDIFF ||
r_type == PPC_RELOC_LOCAL_SECTDIFF ||
r_type == PPC_RELOC_HI16_SECTDIFF ||
r_type == PPC_RELOC_LO16_SECTDIFF ||
r_type == PPC_RELOC_LO14_SECTDIFF ||
r_type == PPC_RELOC_HA16_SECTDIFF){
if(r_scattered != 1){
error_with_cur_obj("relocation entry (%lu) in section "
"(%.16s,%.16s) r_type is PPC_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_ppc)spair_reloc->r_type;
pair_r_value = spair_reloc->r_value;
other_half = spair_reloc->r_address;
}
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 != PPC_RELOC_PAIR){
error_with_cur_obj("relocation entry (%lu) in section "
"(%.16s,%.16s) missing following associated "
"PPC_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 ppc_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 ppc_reloc() symbol index %lu "
"in above file not in undefined map", symbolnum);
}
}
if(refs == NULL &&
((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(refs != NULL){
refs->ref1.ref_type = LIVE_REF_SYMBOL;
refs->ref1.merged_symbol = merged_symbol;
refs->ref2.ref_type = LIVE_REF_NONE;
return;
}
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;
input_pc = section_map->s->addr + r_address;
if(r_type == PPC_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 += input_pc;
}
else{
instruction = get_long((long *)(contents + r_address));
switch(r_type){
case PPC_RELOC_HI16:
offset = ((instruction & 0xffff) << 16) |
other_half;
break;
case PPC_RELOC_LO16:
offset = (other_half << 16) |
(instruction & 0xffff);
break;
case PPC_RELOC_HA16:
if((other_half & 0x00008000) != 0)
offset = ((instruction & 0xffff) << 16) +
(0xffff0000 + other_half);
else
offset = ((instruction & 0xffff) << 16) +
other_half;
break;
case PPC_RELOC_LO14:
offset = (other_half << 16) |
(instruction & 0xfffc);
break;
case PPC_RELOC_BR14:
offset = instruction & 0xfffc;
br14_disp_sign = (offset & 0x8000);
if(br14_disp_sign != 0)
offset |= 0xffff0000;
if(r_pcrel)
offset += input_pc;
break;
case PPC_RELOC_BR24:
offset = instruction & 0x03fffffc;
if((offset & 0x02000000) != 0)
offset |= 0xfc000000;
if(r_pcrel)
offset += input_pc;
break;
case PPC_RELOC_JBSR:
offset = other_half;
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(refs != NULL){
refs->ref1.ref_type = LIVE_REF_NONE;
refs->ref2.ref_type = LIVE_REF_NONE;
return;
}
}
else{
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;
if(local_map->s->flags & S_ATTR_DEBUG){
error_with_cur_obj("illegal reference to debug section,"
" from non-debug section (%.16s,%.16s) via "
"relocation entry (%lu) to section (%.16s,%.16s)",
section_map->s->segname, section_map->s->sectname,
i, local_map->s->segname, local_map->s->sectname);
return;
}
pair_local_map = NULL;
if(r_type == PPC_RELOC_SECTDIFF ||
r_type == PPC_RELOC_LOCAL_SECTDIFF ||
r_type == PPC_RELOC_HI16_SECTDIFF ||
r_type == PPC_RELOC_LO16_SECTDIFF ||
r_type == PPC_RELOC_LO14_SECTDIFF ||
r_type == PPC_RELOC_HA16_SECTDIFF){
pair_local_map =
&(cur_obj->section_maps[pair_r_symbolnum - 1]);
pair_local_map->output_section->referenced = TRUE;
if(pair_local_map->s->flags & S_ATTR_DEBUG){
error_with_cur_obj("illegal reference to debug "
"section, from non-debug section (%.16s,%.16s) "
"via relocation entry (%lu) to section (%.16s,"
"%.16s)", section_map->s->segname,
section_map->s->sectname, i,
pair_local_map->s->segname,
pair_local_map->s->sectname);
return;
}
}
if(local_map->nfine_relocs == 0 &&
(pair_local_map == NULL ||
pair_local_map->nfine_relocs == 0) ){
if(r_type == PPC_RELOC_SECTDIFF ||
r_type == PPC_RELOC_LOCAL_SECTDIFF ||
r_type == PPC_RELOC_HI16_SECTDIFF ||
r_type == PPC_RELOC_LO16_SECTDIFF ||
r_type == PPC_RELOC_LO14_SECTDIFF ||
r_type == PPC_RELOC_HA16_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{
if(r_pcrel){
input_pc = section_map->s->addr +
r_address;
if(section_map->nfine_relocs == 0)
output_pc = section_map->output_section->s.addr
+ section_map->offset +
r_address;
else
output_pc = section_map->output_section->s.addr
+
fine_reloc_output_offset(section_map,
r_address);
}
else{
input_pc = 0;
output_pc = 0;
}
if(r_type == PPC_RELOC_VANILLA ||
r_type == PPC_RELOC_SECTDIFF ||
r_type == PPC_RELOC_LOCAL_SECTDIFF){
switch(r_length){
case 0:
value = get_byte((char *)(contents +
r_address));
break;
case 1:
value = get_short((short *)(contents +
r_address));
break;
case 2:
value = get_long((long *)(contents +
r_address));
break;
default:
break;
}
}
else{
instruction = get_long((long *)(contents +
r_address));
switch(r_type){
case PPC_RELOC_HI16:
case PPC_RELOC_HI16_SECTDIFF:
value = ((instruction & 0xffff) << 16) |
other_half;
break;
case PPC_RELOC_LO16:
case PPC_RELOC_LO16_SECTDIFF:
value = (other_half << 16) |
(instruction & 0xffff);
break;
case PPC_RELOC_HA16:
case PPC_RELOC_HA16_SECTDIFF:
if((other_half & 0x00008000) != 0)
value = ((instruction & 0xffff) << 16) +
(0xffff0000 + other_half);
else
value = ((instruction & 0xffff) << 16) +
other_half;
break;
case PPC_RELOC_LO14:
case PPC_RELOC_LO14_SECTDIFF:
value = (other_half << 16) |
(instruction & 0xfffc);
break;
case PPC_RELOC_BR14:
value = instruction & 0xfffc;
br14_disp_sign = (value & 0x8000);
if(br14_disp_sign != 0)
value |= 0xffff0000;
break;
case PPC_RELOC_BR24:
value = instruction & 0x03fffffc;
if((value & 0x02000000) != 0)
value |= 0xfc000000;
break;
case PPC_RELOC_JBSR:
value = other_half;
break;
default:
break;
}
}
if(r_type == PPC_RELOC_SECTDIFF ||
r_type == PPC_RELOC_LOCAL_SECTDIFF ||
r_type == PPC_RELOC_HI16_SECTDIFF ||
r_type == PPC_RELOC_LO16_SECTDIFF ||
r_type == PPC_RELOC_LO14_SECTDIFF ||
r_type == PPC_RELOC_HA16_SECTDIFF){
value += input_pc;
offset = value - r_value + pair_r_value;
if(refs != NULL){
fine_reloc_output_ref(
local_map,
r_value - local_map->s->addr,
&(refs->ref1) );
fine_reloc_output_ref(
local_map,
pair_r_value - local_map->s->addr,
&(refs->ref2) );
return;
}
if(local_map->nfine_relocs != 0){
legal_reference(section_map, r_address,
local_map, r_value - local_map->s->addr +
offset, i,
r_type != PPC_RELOC_LOCAL_SECTDIFF);
value = fine_reloc_output_address(local_map,
r_value - local_map->s->addr,
local_map->output_section->s.addr);
}
else{
value = local_map->output_section->s.addr +
local_map->offset +
r_value - local_map->s->addr;
}
if(pair_local_map->nfine_relocs != 0){
legal_reference(section_map, r_address,
pair_local_map, pair_r_value -
pair_local_map->s->addr, i, TRUE);
value -=
fine_reloc_output_address(pair_local_map,
pair_r_value - pair_local_map->s->addr,
pair_local_map->output_section->s.addr);
}
else{
value -=
pair_local_map->output_section->s.addr +
pair_local_map->offset +
pair_r_value - pair_local_map->s->addr;
}
value += offset;
value -= output_pc;
}
else{
value += input_pc;
if(r_scattered == 0){
r_value = value;
offset = 0;
}
else{
offset = value - r_value;
}
legal_reference(section_map, r_address, local_map,
r_value - local_map->s->addr + offset, i,
FALSE);
if(refs != NULL){
fine_reloc_output_ref(
local_map,
r_value - local_map->s->addr,
&(refs->ref1) );
refs->ref2.ref_type = LIVE_REF_NONE;
return;
}
value = fine_reloc_output_address(local_map,
r_value - local_map->s->addr,
local_map->output_section->s.addr);
value -= output_pc;
value += offset;
}
if(r_type == PPC_RELOC_VANILLA ||
r_type == PPC_RELOC_LOCAL_SECTDIFF ||
r_type == PPC_RELOC_SECTDIFF){
switch(r_length){
case 0:
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:
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:
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{
switch(r_type){
case PPC_RELOC_HI16:
case PPC_RELOC_HI16_SECTDIFF:
other_half = value & 0xffff;
instruction = (instruction & 0xffff0000) |
((value >> 16) & 0xffff);
break;
case PPC_RELOC_LO16:
case PPC_RELOC_LO16_SECTDIFF:
other_half = (value >> 16) & 0xffff;
instruction = (instruction & 0xffff0000) |
(value & 0xffff);
break;
case PPC_RELOC_HA16:
case PPC_RELOC_HA16_SECTDIFF:
other_half = value & 0xffff;
if((value & 0x00008000) != 0)
instruction = (instruction & 0xffff0000) |
(((value + 0x00008000) >> 16) & 0xffff);
else
instruction = (instruction & 0xffff0000) |
((value >> 16) & 0xffff);
break;
case PPC_RELOC_LO14:
case PPC_RELOC_LO14_SECTDIFF:
if((value & 0x3) != 0)
error_with_cur_obj("relocation error "
"for relocation entry %lu in section "
"(%.16s,%.16s) (relocated value not a "
"multiple of 4 bytes)", i,
section_map->s->segname,
section_map->s->sectname);
other_half = (value >> 16) & 0xffff;
instruction = (instruction & 0xffff0003) |
(value & 0xfffc);
break;
case PPC_RELOC_BR14:
br14_disp_sign = (instruction & 0x8000);
if((value & 0x3) != 0)
error_with_cur_obj("relocation error "
"for relocation entry %lu in section "
"(%.16s,%.16s) (displacement not a "
"multiple of 4 bytes)", i,
section_map->s->segname,
section_map->s->sectname);
if((value & 0xffff8000) != 0xffff8000 &&
(value & 0xffff8000) != 0x00000000)
error_with_cur_obj("relocation overflow "
"for relocation entry %lu in section "
"(%.16s,%.16s) (displacement too large)"
, i, section_map->s->segname,
section_map->s->sectname);
instruction = (instruction & 0xffff0003) |
(value & 0xfffc);
if(r_length == 3 &&
(instruction & 0xfc000000) == 0x40000000 &&
(instruction & 0x03e00000) != 0x02800000 &&
(instruction & 0x00008000) != br14_disp_sign)
instruction ^= (1 << 21);
break;
case PPC_RELOC_BR24:
if((value & 0x3) != 0)
error_with_cur_obj("relocation error "
"for relocation entry %lu in section "
"(%.16s,%.16s) (displacement not a "
"multiple of 4 bytes)", i,
section_map->s->segname,
section_map->s->sectname);
if((value & 0xfe000000) != 0xfe000000 &&
(value & 0xfe000000) != 0x00000000)
error_with_cur_obj("relocation overflow "
"for relocation entry %lu in section "
"(%.16s,%.16s) (displacement too large)"
, i, section_map->s->segname,
section_map->s->sectname);
instruction = (instruction & 0xfc000003) |
(value & 0x03fffffc);
break;
case PPC_RELOC_JBSR:
other_half = value;
if(section_map->nfine_relocs == 0)
value -= section_map->output_section->s.addr
+ section_map->offset + r_address;
else
value -= section_map->output_section->s.addr
+ fine_reloc_output_offset(
section_map, r_address);
if(save_reloc == 0 &&
(output_for_dyld == FALSE || r_extern == 0 ||
(merged_symbol->nlist.n_type & N_TYPE) !=
N_UNDF) &&
(U_ABS(value) <= 0x01ffffff)){
instruction = (instruction & 0xfc000003) |
(value & 0x03fffffc);
}
break;
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);
return;
}
set_long((long *)(contents + r_address),
instruction);
}
goto update_reloc;
}
}
}
if(r_pcrel){
if(section_map->nfine_relocs == 0)
value += + section_map->s->addr
- (section_map->output_section->s.addr +
section_map->offset );
else
value += + section_map->s->addr + r_address
- (section_map->output_section->s.addr +
fine_reloc_output_offset(section_map,
r_address));
}
if(r_type == PPC_RELOC_VANILLA ||
r_type == PPC_RELOC_LOCAL_SECTDIFF ||
r_type == PPC_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 PPC_RELOC_HI16:
case PPC_RELOC_HI16_SECTDIFF:
immediate = ((instruction & 0xffff) << 16) |
other_half;
immediate += value;
instruction = (instruction & 0xffff0000) |
((immediate >> 16) & 0xffff);
other_half = immediate & 0xffff;
break;
case PPC_RELOC_LO16:
case PPC_RELOC_LO16_SECTDIFF:
immediate = (other_half << 16) |
(instruction & 0xffff);
immediate += value;
instruction = (instruction & 0xffff0000) |
(immediate & 0xffff);
other_half = (immediate >> 16) & 0xffff;
break;
case PPC_RELOC_HA16:
case PPC_RELOC_HA16_SECTDIFF:
if((other_half & 0x00008000) != 0)
immediate = ((instruction & 0xffff) << 16) +
(0xffff0000 + other_half);
else
immediate = ((instruction & 0xffff) << 16) +
(other_half);
immediate += value;
if((immediate & 0x00008000) != 0)
instruction = (instruction & 0xffff0000) |
(((immediate + 0x00008000) >> 16) & 0xffff);
else
instruction = (instruction & 0xffff0000) |
((immediate >> 16) & 0xffff);
other_half = immediate & 0xffff;
break;
case PPC_RELOC_LO14:
case PPC_RELOC_LO14_SECTDIFF:
immediate = (other_half << 16) |
(instruction & 0xfffc);
immediate += value;
if((immediate & 0x3) != 0)
error_with_cur_obj("relocation error for relocation "
"entry %lu in section (%.16s,%.16s) (relocated "
"value not a multiple of 4 bytes)", i,
section_map->s->segname, section_map->s->sectname);
instruction = (instruction & 0xffff0003) |
(immediate & 0xfffc);
other_half = (immediate >> 16) & 0xffff;
break;
case PPC_RELOC_BR14:
br14_disp_sign = (instruction & 0x8000);
immediate = instruction & 0xfffc;
if((immediate & 0x8000) != 0)
immediate |= 0xffff0000;
immediate += value;
if((immediate & 0x3) != 0)
error_with_cur_obj("relocation error for relocation "
"entry %lu in section (%.16s,%.16s) (displacement "
"not a multiple of 4 bytes)", i,
section_map->s->segname, section_map->s->sectname);
if((immediate & 0xffff8000) != 0xffff8000 &&
(immediate & 0xffff8000) != 0x00000000)
error_with_cur_obj("relocation overflow for relocation "
"entry %lu in section (%.16s,%.16s) (displacement "
"too large)", i, section_map->s->segname,
section_map->s->sectname);
instruction = (instruction & 0xffff0003) |
(immediate & 0xfffc);
if(r_length == 3 &&
(instruction & 0xfc000000) == 0x40000000 &&
(instruction & 0x03e00000) != 0x02800000 &&
(instruction & 0x00008000) != br14_disp_sign)
instruction ^= (1 << 21);
break;
break;
case PPC_RELOC_BR24:
immediate = instruction & 0x03fffffc;
if((immediate & 0x02000000) != 0)
immediate |= 0xfc000000;
immediate += value;
if((immediate & 0x3) != 0)
error_with_cur_obj("relocation error for relocation "
"entry %lu in section (%.16s,%.16s) (displacement "
"not a multiple of 4 bytes)", i,
section_map->s->segname, section_map->s->sectname);
if((immediate & 0xfe000000) != 0xfe000000 &&
(immediate & 0xfe000000) != 0x00000000)
error_with_cur_obj("relocation overflow for relocation "
"entry %lu in section (%.16s,%.16s) (displacement "
"too large)", i, section_map->s->segname,
section_map->s->sectname);
instruction = (instruction & 0xfc000003) |
(immediate & 0x03fffffc);
break;
case PPC_RELOC_JBSR:
value += other_half;
other_half = value;
if(section_map->nfine_relocs == 0)
value += - (section_map->output_section->s.addr +
section_map->offset + r_address);
else
value += - (section_map->output_section->s.addr +
fine_reloc_output_offset(section_map,
r_address));
if(save_reloc == 0 &&
(output_for_dyld == FALSE || r_extern == 0 ||
(merged_symbol->nlist.n_type & N_TYPE) != N_UNDF) &&
(U_ABS(value) <= 0x01ffffff)){
instruction = (instruction & 0xfc000003) |
(value & 0x03fffffc);
}
break;
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);
}
update_reloc:
;
#ifndef RLD
if(save_reloc || output_for_dyld){
if(r_extern){
if(prebinding == TRUE &&
(section_map->s->flags & SECTION_TYPE) ==
S_LAZY_SYMBOL_POINTERS){
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 = PPC_RELOC_PB_LA_PTR;
sreloc->r_value = value;
}
else 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;
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 || output_for_dyld){
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(prebinding == TRUE &&
(section_map->s->flags & SECTION_TYPE) ==
S_LAZY_SYMBOL_POINTERS){
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 = PPC_RELOC_PB_LA_PTR;
sreloc->r_value = value;
}
else if(reloc->r_symbolnum != R_ABS){
if(local_map->nfine_relocs == 0){
reloc->r_symbolnum =
local_map->output_section->output_sectnum;
}
else{
reloc->r_symbolnum =
fine_reloc_output_sectnum(local_map,
r_value - local_map->s->addr);
}
}
}
else{
if(output_for_dyld){
reloc = (struct relocation_info *)sreloc;
r_scattered = 0;
reloc->r_address = r_address;
reloc->r_pcrel = r_pcrel;
reloc->r_extern = 0;
reloc->r_length = r_length;
reloc->r_type = r_type;
if(local_map->nfine_relocs == 0){
reloc->r_symbolnum =
local_map->output_section->output_sectnum;
}
else{
reloc->r_symbolnum =
fine_reloc_output_sectnum(local_map,
r_value - local_map->s->addr);
}
}
else{
if(local_map->nfine_relocs == 0)
sreloc->r_value +=
- local_map->s->addr
+ local_map->output_section->s.addr +
local_map->offset;
else
sreloc->r_value =
fine_reloc_output_address(local_map,
r_value - local_map->s->addr,
local_map->output_section->s.addr);
}
}
if(r_scattered == 0){
if(section_map->nfine_relocs == 0){
reloc->r_address += section_map->offset;
}
else{
reloc->r_address = fine_reloc_output_offset(section_map,
r_address);
}
}
else{
if(section_map->nfine_relocs == 0){
sreloc->r_address += section_map->offset;
}
else{
sreloc->r_address =fine_reloc_output_offset(section_map,
r_address);
}
}
if(pair_r_type == PPC_RELOC_PAIR){
if(pair_reloc != NULL){
if(r_type == PPC_RELOC_JBSR &&
(other_half & 0x80000000) == 0x80000000){
sreloc = (struct scattered_relocation_info *)
pair_reloc;
r_scattered = 1;
sreloc->r_scattered = r_scattered;
sreloc->r_address = 0;
sreloc->r_pcrel = r_pcrel;
sreloc->r_length = r_length;
sreloc->r_type = PPC_RELOC_PAIR;
sreloc->r_value = other_half;
}
else{
pair_reloc->r_address = other_half;
}
}
else if(spair_reloc != NULL){
if(r_type == PPC_RELOC_SECTDIFF ||
r_type == PPC_RELOC_LOCAL_SECTDIFF ||
r_type == PPC_RELOC_HI16_SECTDIFF ||
r_type == PPC_RELOC_LO16_SECTDIFF ||
r_type == PPC_RELOC_LO14_SECTDIFF ||
r_type == PPC_RELOC_HA16_SECTDIFF){
if(pair_local_map->nfine_relocs == 0)
spair_reloc->r_value +=
- pair_local_map->s->addr
+ (pair_local_map->output_section->s.addr +
pair_local_map->offset);
else
spair_reloc->r_value =
fine_reloc_output_address(pair_local_map,
pair_r_value - pair_local_map->s->addr,
pair_local_map->output_section->s.addr);
if(r_type == PPC_RELOC_HI16_SECTDIFF ||
r_type == PPC_RELOC_LO16_SECTDIFF ||
r_type == PPC_RELOC_LO14_SECTDIFF ||
r_type == PPC_RELOC_HA16_SECTDIFF)
spair_reloc->r_address = other_half;
}
else{
if(r_type == PPC_RELOC_JBSR)
spair_reloc->r_value = other_half;
else
spair_reloc->r_address = other_half;
}
}
else{
fatal("internal error, in ppc_reloc() pair_r_type "
"is PPC_RELOC_PAIR but pair_reloc and spair_reloc "
"are NULL");
}
}
}
#endif
if(pair_r_type == PPC_RELOC_PAIR)
i++;
}
}