coalesced_sections.c [plain text]
#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 <stdarg.h>
#include <string.h>
#include "stuff/openstep_mach.h"
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/stab.h>
#include <mach-o/reloc.h>
#include <mach-o/ppc/reloc.h>
#include <mach-o/hppa/reloc.h>
#include "stuff/arch.h"
#include "stuff/reloc.h"
#include "ld.h"
#include "live_refs.h"
#include "objects.h"
#include "sections.h"
#include "pass2.h"
#include "generic_reloc.h"
#include "pass1.h"
#include "symbols.h"
#include "layout.h"
#include "coalesced_sections.h"
#include "dylibs.h"
__private_extern__
void
coalesced_section_merge(
void *data,
struct merged_section *ms,
struct section *s,
struct section_map *section_map,
enum bool redo_live)
{
unsigned long i, j, nsect, count;
struct nlist *object_symbols;
char *object_strings;
enum bool start_section;
struct load_order *load_orders;
struct fine_reloc *fine_relocs;
struct merged_symbol *merged_symbol;
struct relocation_info *relocs, reloc;
struct scattered_relocation_info *sreloc, *spair_reloc;
unsigned long r_address, r_pcrel, r_length, r_type, pair_r_type, r_extern,
r_symbolnum, r_scattered, pair, r_value;
struct undefined_map *undefined_map;
#ifndef RLD
enum bool pic;
#endif
enum bool defined, force_extern_reloc;
pair_r_type = 0;
if(s->size == 0)
return;
merged_symbol = NULL;
nsect = 0;
for(i = 0; i < cur_obj->nsection_maps; i++){
if(&(cur_obj->section_maps[i]) == section_map){
nsect = i + 1;
break;
}
}
if(nsect == 0){
fatal("internal error: coalesced_section_merge() called "
"with bad section_map for section (%.16s,%.16s)",
s->segname, s->sectname);
}
count = 0;
start_section = FALSE;
if(cur_obj->symtab != NULL){
object_symbols = (struct nlist *)(cur_obj->obj_addr +
cur_obj->symtab->symoff);
object_strings = (char *)(cur_obj->obj_addr +
cur_obj->symtab->stroff);
}
else{
object_symbols = NULL;
object_strings = NULL;
}
if(redo_live == TRUE)
goto set_load_orders;
if(cur_obj->symtab != NULL){
for(i = 0; i < cur_obj->symtab->nsyms; i++){
if((object_symbols[i].n_type & N_TYPE) == N_SECT &&
object_symbols[i].n_sect == nsect &&
(object_symbols[i].n_type & N_STAB) == 0){
count++;
#ifdef notdef
if((object_symbols[i].n_type & (N_EXT|N_PEXT)) ==
(N_EXT|N_PEXT)){
error_with_cur_obj("malformed object (symbol: %s in "
"S_COALESCE section (%.16s,%.16s) can't be a "
"private extern symbol)",
object_strings + object_symbols[i].n_un.n_strx,
s->segname, s->sectname);
return;
}
if((object_symbols[i].n_type & N_EXT) != N_EXT){
error_with_cur_obj("malformed object (symbol: %s in "
"S_COALESCE section (%.16s,%.16s) not an external "
"symbol)",
object_strings + object_symbols[i].n_un.n_strx,
s->segname, s->sectname);
return;
}
#endif
if(object_symbols[i].n_value == s->addr)
start_section = TRUE;
}
}
}
if(start_section == FALSE){
error_with_cur_obj("malformed object (section (%.16s,%.16s) no "
"symbol at start of coalesced section)",s->segname,s->sectname);
return;
}
set_load_orders:
if(redo_live == FALSE){
load_orders = allocate(sizeof(struct load_order) * count);
memset(load_orders, '\0', sizeof(struct load_order) * count);
section_map->load_orders = load_orders;
section_map->nload_orders = count;
}
else{
load_orders = section_map->load_orders;
count = section_map->nload_orders;
goto deal_with_contents;
}
j = 0;
for(i = 0; i < cur_obj->symtab->nsyms; i++){
if((object_symbols[i].n_type & N_TYPE) == N_SECT &&
object_symbols[i].n_sect == nsect &&
(object_symbols[i].n_type & N_STAB) == 0){
load_orders[j].name = object_strings +
object_symbols[i].n_un.n_strx;
load_orders[j].value =
object_symbols[i].n_value;
load_orders[j].global_coalesced_symbol =
(object_symbols[i].n_type & N_EXT) == N_EXT;
j++;
}
}
#ifdef DEBUG
if(debug & (1 << 14))
print_load_order(load_orders, count, ms,
cur_obj, "names and values");
#endif
qsort(load_orders,
count,
sizeof(struct load_order),
(int (*)(const void *, const void *))qsort_load_order_values);
for(i = 0; i < count - 1; i++){
load_orders[i].input_offset =
load_orders[i].value -
s->addr;
load_orders[i].input_size =
load_orders[i + 1].value -
load_orders[i].value;
}
load_orders[i].input_offset =
load_orders[i].value -
s->addr;
load_orders[i].input_size =
s->addr + s->size -
load_orders[i].value;
#ifdef DEBUG
if(debug & (1 << 15))
print_load_order(load_orders, count, ms,
cur_obj, "sizes and offsets");
#endif
deal_with_contents:
if(redo_live == FALSE){
fine_relocs = allocate(count * sizeof(struct fine_reloc));
memset(fine_relocs, '\0', count * sizeof(struct fine_reloc));
section_map->fine_relocs = fine_relocs;
section_map->nfine_relocs = count;
}
else{
fine_relocs = section_map->fine_relocs;
count = section_map->nfine_relocs;
}
for(i = 0; i < count; i++){
if(redo_live == FALSE)
fine_relocs[i].input_offset = load_orders[i].input_offset;
if(load_orders[i].global_coalesced_symbol == TRUE){
merged_symbol = lookup_symbol(load_orders[i].name);
if(merged_symbol->name_len == 0)
fatal("internal error, coalesced_section_merge() failed in "
"looking up external symbol: %s",load_orders[i].name);
if(merged_symbol->definition_object == cur_obj){
if(redo_live == FALSE){
fine_relocs[i].use_contents = TRUE;
fine_relocs[i].indirect_defined = FALSE;
}
if(redo_live == FALSE || fine_relocs[i].live == TRUE){
ms->s.size = align_to_input_mod(ms->s.size,
fine_relocs[i].input_offset,
ms->s.align);
fine_relocs[i].output_offset = ms->s.size;
ms->s.size += load_orders[i].input_size;
}
}
else{
if(redo_live == FALSE){
fine_relocs[i].use_contents = FALSE;
fine_relocs[i].indirect_defined = FALSE;
fine_relocs[i].merged_symbol = merged_symbol;
}
}
if((merged_symbol->nlist.n_type & N_PEXT) == N_PEXT){
if(redo_live == FALSE)
fine_relocs[i].local_symbol = TRUE;
}
else{
if(redo_live == FALSE)
fine_relocs[i].local_symbol = FALSE;
}
}
else{
if(redo_live == FALSE){
fine_relocs[i].use_contents = TRUE;
fine_relocs[i].local_symbol = TRUE;
fine_relocs[i].indirect_defined = FALSE;
}
if(redo_live == FALSE || fine_relocs[i].live == TRUE){
ms->s.size = align_to_input_mod(ms->s.size,
fine_relocs[i].input_offset,
ms->s.align);
fine_relocs[i].output_offset = ms->s.size;
ms->s.size += load_orders[i].input_size;
}
}
}
#ifdef COALESCE_DEBUG
printf("Fine relocs created for coalesced section (%.16s,%.16s) in ",
s->segname, s->sectname);
print_obj_name(cur_obj);
printf("\n");
for(i = 0; i < count; i++){
printf("fine_relocs[%lu] load_orders[%lu].name = %s\n",
i, i, load_orders[i].name);
printf("\tuse_contents = %s\n", fine_relocs[i].use_contents == TRUE
? "TRUE" : "FALSE");
printf("\tlocal_symbol = %s\n", fine_relocs[i].local_symbol == TRUE
? "TRUE" : "FALSE");
printf("\tinput_offset = %d\n", fine_relocs[i].input_offset);
if(fine_relocs[i].use_contents == TRUE)
printf("\toutput_offset = %ld\n", fine_relocs[i].output_offset);
else{
merged_symbol = fine_relocs[i].merged_symbol;
printf("\t%s from ", merged_symbol->nlist.n_un.n_name);
print_obj_name(merged_symbol->definition_object);
printf("\n");
}
}
#endif
relocs = (struct relocation_info *)(cur_obj->obj_addr + s->reloff);
for(i = 0; i < s->nreloc; i++){
reloc = relocs[i];
if(cur_obj->swapped &&
section_map->input_relocs_already_swapped == FALSE)
swap_relocation_info(&reloc, 1, host_byte_sex);
if((reloc.r_address & R_SCATTERED) != 0){
sreloc = (struct scattered_relocation_info *)(&reloc);
r_scattered = 1;
r_address = sreloc->r_address;
r_pcrel = sreloc->r_pcrel;
r_length = sreloc->r_length;
r_type = sreloc->r_type;
r_extern = 0;
r_value = sreloc->r_value;
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{
r_scattered = 0;
r_address = reloc.r_address;
r_pcrel = reloc.r_pcrel;
r_length = reloc.r_length;
r_type = reloc.r_type;
r_extern = reloc.r_extern;
r_symbolnum = reloc.r_symbolnum;
}
if(r_type == reloc_pair_r_type(arch_flag.cputype)){
error_with_cur_obj("malformed object (stray relocation PAIR "
"entry (%lu) in section (%.16s,%.16s))", i, s->segname,
s->sectname);
continue;
}
if(r_address >= s->size){
error_with_cur_obj("malformed object (r_address (0x%x) field "
"of relocation entry %ld in section (%.16s,%.16s) out of "
"range)",(unsigned int)r_address, i,s->segname,s->sectname);
continue;
}
if(reloc_has_pair(arch_flag.cputype, r_type)){
if(i + 1 < s->nreloc){
reloc = relocs[i + 1];
if(cur_obj->swapped &&
section_map->input_relocs_already_swapped == FALSE)
swap_relocation_info(&reloc, 1, host_byte_sex);
if((reloc.r_address & R_SCATTERED) != 0){
spair_reloc = (struct scattered_relocation_info *)
&reloc;
pair_r_type = spair_reloc->r_type;
}
else{
pair_r_type = reloc.r_type;
}
}
if(i + 1 >= s->nreloc ||
pair_r_type != reloc_pair_r_type(arch_flag.cputype)){
error_with_cur_obj("malformed object (relocation entry "
"(%lu) in section (%.16s,%.16s) missing following "
"associated PAIR entry)", i, s->segname, s->sectname);
continue;
}
}
defined = TRUE;
force_extern_reloc = FALSE;
if(output_for_dyld && 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, s->segname, s->sectname);
continue;
}
undefined_map = bsearch(&r_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;
if((merged_symbol->nlist.n_type & N_TYPE) == N_SECT &&
(merged_symbol->definition_object->section_maps[
merged_symbol->nlist.n_sect - 1].s->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;
}
}
else{
if((object_symbols[r_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", r_symbolnum, i,
section_map->s->segname, section_map->s->sectname);
return;
}
if((object_symbols[r_symbolnum].n_type & N_TYPE) ==
N_SECT &&
(cur_obj->section_maps[object_symbols[r_symbolnum].
n_sect-1].s->flags & SECTION_TYPE) == S_COALESCED){
merged_symbol = lookup_symbol(object_strings +
object_symbols[r_symbolnum].n_un.n_strx);
if(merged_symbol->name_len == 0){
fatal("internal error, in coalesced_section_merge()"
" failed to lookup coalesced symbol %s",
object_strings +
object_symbols[r_symbolnum].n_un.n_strx);
}
if(((merged_symbol->nlist.n_type & N_TYPE) == N_SECT &&
(merged_symbol->definition_object->section_maps[
merged_symbol->nlist.n_sect-1].
s->flags & SECTION_TYPE) != S_COALESCED) ||
((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;
}
else{
if(object_symbols[r_symbolnum].n_type !=
(N_EXT | 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", r_symbolnum, i,
section_map->s->segname,
section_map->s->sectname);
return;
}
print_obj_name(cur_obj);
fatal("internal error, in coalesced_section_merge() "
"symbol index %lu in above file not in undefined "
"map", r_symbolnum);
}
}
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_EXT | N_UNDF) ||
merged_symbol->nlist.n_type == (N_EXT | N_PBUD) ||
(merged_symbol->nlist.n_type == (N_EXT | N_INDR) &&
merged_symbol->defined_in_dylib == TRUE))
defined = FALSE;
else
defined = TRUE;
}
if(reloc_has_pair(arch_flag.cputype, r_type))
pair = 1;
else
pair = 0;
if(output_for_dyld &&
((arch_flag.cputype == CPU_TYPE_POWERPC &&
r_type == PPC_RELOC_JBSR) ||
(arch_flag.cputype == CPU_TYPE_HPPA &&
r_type == HPPA_RELOC_JBSR)) ){
i += pair;
continue;
}
#ifndef RLD
if(output_for_dyld &&
fine_reloc_offset_in_output(section_map, r_address)){
if(dead_strip == FALSE || redo_live == TRUE)
ms->relocated = TRUE;
if(r_extern == 0)
pic = (enum bool)
(reloc_is_sectdiff(arch_flag.cputype, r_type) ||
(r_pcrel == 1 && r_symbolnum != NO_SECT));
else
pic = (enum bool)
(r_pcrel == 1 &&
(merged_symbol->nlist.n_type & N_TYPE) == N_SECT);
if(filetype == MH_DYLIB && multi_module_dylib == TRUE){
if(r_extern && (merged_symbol->nlist.n_type & N_PEXT) == 0)
section_map->nextrel += 1 + pair;
else if(pic == FALSE)
section_map->nlocrel += 1 + pair;
}
else if(has_dynamic_linker_command){
if(r_extern && defined == FALSE)
section_map->nextrel += 1 + pair;
}
else{
if(r_extern && (merged_symbol->nlist.n_type & N_PEXT) == 0){
if(defined == FALSE || force_extern_reloc == TRUE)
section_map->nextrel += 1 + pair;
else if(pic == FALSE)
section_map->nlocrel += 1 + pair;
}
else if(pic == FALSE)
section_map->nlocrel += 1 + pair;
}
}
else if(save_reloc &&
fine_reloc_offset_in_output(section_map, r_address)){
ms->s.nreloc += 1 + pair;
nreloc += 1 + pair;
}
#endif
i += pair;
}
if(ms->s.nreloc != 0){
if(dead_strip == FALSE || redo_live == TRUE)
ms->relocated = TRUE;
}
if(dead_strip == FALSE)
discard_local_symbols_for_section(nsect, object_symbols,
object_strings, s, section_map);
if(dead_strip == FALSE || redo_live == TRUE){
free(load_orders);
section_map->load_orders = NULL;
section_map->nload_orders = 0;
}
}
__private_extern__
void
coalesced_section_order(
void *data,
struct merged_section *ms)
{
#ifndef RLD
kern_return_t r;
#ifdef __MWERKS__
struct coalesced_section_data *dummy1;
struct merged_section *dummy2;
dummy1 = data;
dummy2 = ms;
#endif
warning("section ordering for coalesced sections not supported ("
"-sectorder %s %s %s ignored)", ms->s.segname, ms->s.sectname,
ms->order_filename);
if((r = vm_deallocate(mach_task_self(), (vm_address_t)
ms->order_addr, ms->order_size)) != KERN_SUCCESS)
mach_fatal(r, "can't vm_deallocate() memory for -sectorder "
"file: %s for section (%.16s,%.16s)",
ms->order_filename, ms->s.segname,
ms->s.sectname);
ms->order_addr = NULL;
#else
#ifdef __MWERKS__
struct coalesced_section_data *dummy1;
struct merged_section *dummy2;
dummy1 = data;
dummy2 = ms;
#endif
#endif
}
__private_extern__
void
coalesced_section_reset_live(
void *data,
struct merged_section *ms)
{
ms->s.size = 0;
if(output_for_dyld){
ms->nlocrel = 0;
ms->nextrel = 0;
}
else if(save_reloc){
nreloc -= ms->s.nreloc;
ms->s.nreloc = 0;
}
}