#import <libc.h>
#ifndef __OPENSTEP__
extern int add_profil(char *, int, int, int);
#endif
#import <stdlib.h>
#import <stdio.h>
#import <string.h>
#import <errno.h>
#import <limits.h>
#import <sys/file.h>
#import <sys/types.h>
#import <sys/stat.h>
#import <mach/mach.h>
#import "stuff/openstep_mach.h"
#import <mach-o/fat.h>
#import <mach-o/loader.h>
#import <mach-o/dyld_debug.h>
#import <mach-o/dyld_gdb.h>
#ifdef hppa
#import <mach-o/hppa/reloc.h>
#endif
#ifdef sparc
#import <mach-o/sparc/reloc.h>
#endif
#ifdef __ppc__
#import <mach-o/ppc/reloc.h>
#endif
#import <sys/mman.h>
#ifdef __OPENSTEP__
extern int mmap(char *, int, int, int, int, int);
extern char *realpath(const char *pathname, char resolvedname[MAXPATHLEN]);
#import <mach-o/gmon.h>
#else
#import <sys/gmon.h>
#endif
#define SCALE_1_TO_1 0x10000L
#if !defined(__GONZO_BUNSEN_BEAKER__) && !defined(__HERA__) && defined(__ppc__)
#include <architecture/ppc/processor_facilities.h>
#endif
#ifdef __MACH30__
#include <pthread.h>
#endif
#import "stuff/bool.h"
#import "stuff/best_arch.h"
#import "stuff/bytesex.h"
#import "stuff/round.h"
#ifndef __MACH30__
#import "../profileServer/profileServer.h"
#endif
#import "inline_strcmp.h"
#import "images.h"
#import "reloc.h"
#import "symbols.h"
#import "errors.h"
#import "allocate.h"
#import "dyld_init.h"
#import "entry_point.h"
#import "debug.h"
#import "register_funcs.h"
#import "lock.h"
#import "mod_init_funcs.h"
struct object_images object_images;
struct library_images library_images;
char *executables_name = NULL;
static enum bool loading_executables_libraries = TRUE;
static enum bool init_routine_being_called = FALSE;
enum string_block_size { STRING_BLOCK_SIZE = (MAXPATHLEN * 2) + 2 };
struct string_block {
unsigned long used;
char strings[STRING_BLOCK_SIZE];
};
static struct string_block string_block;
enum module_state_block_size { MODULE_STATE_BLOCK_SIZE = 2000 };
struct module_state_block {
unsigned long used;
module_state module_states[MODULE_STATE_BLOCK_SIZE];
};
static struct module_state_block module_state_block;
static module_state *allocate_module_states(
unsigned long nmodules);
static void deallocate_module_states(
module_state *modules,
unsigned long nmodules);
enum image_pointers_block_size { IMAGE_POINTER_BLOCK_SIZE = 10 };
struct image_pointers_block {
unsigned long used;
struct image * image_pointers[IMAGE_POINTER_BLOCK_SIZE];
};
static struct image_pointers_block image_pointers_block;
static struct image ** allocate_image_pointers(
unsigned long n);
static void reallocate_image_pointers(
struct image **image_pointers,
unsigned long old_size,
unsigned long new_size);
static void deallocate_image_pointers(
struct image **image_pointers,
unsigned long n);
void (*dyld_monaddition)(char *lowpc, char *highpc) = NULL;
static struct object_image *new_object_image(
void);
static struct library_image *new_library_image(
unsigned long nmodules);
static char *get_framework_name(
char *name,
unsigned long *short_name_size,
enum bool with_underbar_suffix);
static char * get_library_name(
char *name,
unsigned long *short_name_size);
static char *look_back_for_slash(
char *name,
char *p);
static char *search_for_name_in_path(
char *name,
char *path,
char *suffix);
static char *construct_at_executable_path_library(
char *dylib_name);
static enum bool map_library_image(
struct dylib_command *dl,
char *dylib_name,
int fd,
char *file_addr,
unsigned long file_size,
unsigned long library_offset,
unsigned long library_size,
dev_t dev,
ino_t ino,
struct image **image_pointer);
static enum bool validate_library(
char *dylib_name,
struct dylib_command *dl,
struct library_image *li);
static enum bool set_prebound_state(
struct prebound_dylib_command *pbdylib);
static enum bool check_image(
char *name,
char *image_type,
unsigned long image_size,
struct mach_header *mh,
struct segment_command **linkedit_segment,
struct segment_command **mach_header_segment,
struct dysymtab_command **dyst,
struct symtab_command **st,
struct dylib_command **dlid,
struct routines_command **rc,
unsigned long *low_addr,
unsigned long *high_addr);
static enum bool check_linkedit_info(
char *name,
char *image_type,
struct segment_command *linkedit_segment,
struct symtab_command *st,
struct dysymtab_command *dyst,
struct routines_command *rc);
static enum bool map_image(
char *name,
char *image_type,
unsigned long image_size,
int fd,
char *file_addr,
unsigned long file_size,
unsigned long library_offset,
unsigned long library_size,
unsigned long low_addr,
unsigned long high_addr,
struct mach_header **mh,
struct segment_command **linkedit_segment,
struct dysymtab_command **dyst,
struct symtab_command **st,
struct dylib_command **dlid,
struct routines_command **rc,
struct twolevel_hints_command **hints_cmd,
enum bool *change_protect_on_reloc,
enum bool *cache_sync_on_reloc,
enum bool *has_coalesced_sections,
struct section **init,
struct section **term,
unsigned long *seg1addr,
unsigned long *segs_read_write_addr,
unsigned long *slide_value,
unsigned long *images_dyld_stub_binding_helper);
static void set_segment_protections(
char *name,
char *image_type,
struct mach_header *mh,
unsigned long slide_value);
static enum bool load_images_libraries(
struct mach_header *mh,
struct image *image);
static enum bool setup_sub_images(
struct library_image *library_image);
static enum bool check_time_stamp(
struct library_image *library_image,
struct image *sub_image);
static void setup_umbrella_images(
struct library_image *library_image,
unsigned long max_libraries);
static void unload_shared_file(
struct library_image *library_image);
static void undo_prebinding_for_library(
struct library_image *library_image);
static void failed_use_prebound_libraries(
void);
static void reset_module_states(
void);
static void call_dependent_init_routines(
struct library_image *library_image,
struct image *image,
module_state *module,
enum bool use_header_dependencies);
#ifdef __MACH30__
static void setup_for_lazy_init_routines(
void);
static exception_mask_t old_exception_masks[1];
static exception_port_t old_exception_ports[1];
static exception_behavior_t old_behaviors[1];
static thread_state_flavor_t old_flavors[1];
struct internal_catch_exc_subsystem {
struct subsystem * subsystem;
mach_msg_id_t start;
mach_msg_id_t end;
unsigned int maxsize;
vm_address_t base_addr;
struct routine_descriptor
routine[3];
struct routine_arg_descriptor
arg_descriptor[16];
};
extern struct internal_catch_exc_subsystem internal_catch_exc_subsystem;
#define MY_MSG_SIZE internal_catch_exc_subsystem.maxsize
extern boolean_t exc_server(
mach_msg_header_t * in_msg,
mach_msg_header_t * out_msg);
extern kern_return_t exception_raise(
exception_port_t exception_port,
thread_port_t thread,
task_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count);
extern kern_return_t exception_raise_state(
exception_port_t exception_port,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count,
thread_state_flavor_t *flavor,
thread_state_t in_state,
mach_msg_type_number_t in_state_count,
thread_state_t *out_state,
mach_msg_type_number_t *out_state_count);
extern kern_return_t exception_raise_state_identity(
exception_port_t exception_port,
thread_port_t thread,
task_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count,
thread_state_flavor_t *flavor,
thread_state_t in_state,
mach_msg_type_number_t in_state_count,
thread_state_t *out_state,
mach_msg_type_number_t *out_state_count);
static void exception_server_loop(
mach_port_t my_exception_port);
static enum bool call_lazy_init_routine_for_address(
unsigned long address);
#endif __MACH30__
#ifdef DEBUG_LAZY_INIT_EXCEPTIONS
static const char * exception_name(
exception_type_t exception);
#endif
extern long stub_binding_helper_interface;
extern long _dyld_func_lookup;
void
load_executable_image(
char *name,
struct mach_header *mh,
unsigned long *entry_point)
{
unsigned long i, j, seg1addr;
struct load_command *lc, *load_commands;
struct segment_command *sg, *linkedit_segment;
struct section *s, *init, *term;
struct symtab_command *st;
struct dysymtab_command *dyst;
struct thread_command *thread_command;
struct twolevel_hints_command *hints_cmd;
struct object_image *object_image;
enum bool change_protect_on_reloc, cache_sync_on_reloc,
has_coalesced_sections, seg1addr_found;
char *dylib_name, *p;
#ifdef __ppc__
unsigned long images_dyld_stub_binding_helper;
images_dyld_stub_binding_helper =
(unsigned long)(&unlinked_lazy_pointer_handler);
#endif
executables_name = name;
linkedit_segment = NULL;
st = NULL;
dyst = NULL;
hints_cmd = NULL;
init = NULL;
term = NULL;
seg1addr_found = FALSE;
seg1addr = 0;
change_protect_on_reloc = FALSE;
cache_sync_on_reloc = FALSE;
has_coalesced_sections = FALSE;
load_commands = (struct load_command *)
((char *)mh + sizeof(struct mach_header));
lc = load_commands;
for(i = 0; i < mh->ncmds; i++){
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
if(strcmp(sg->segname, SEG_LINKEDIT) == 0){
if(linkedit_segment == NULL)
linkedit_segment = sg;
}
if(seg1addr_found == FALSE){
seg1addr = sg->vmaddr;
seg1addr_found = TRUE;
}
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if(strcmp(s->segname, "__DATA") == 0 &&
strcmp(s->sectname, "__dyld") == 0){
if(s->size >= sizeof(unsigned long)){
*((long *)s->addr) =
(long)&stub_binding_helper_interface;
}
if(s->size >= 2 * sizeof(unsigned long)){
*((long *)(s->addr + 4)) =
(long)&_dyld_func_lookup;
}
if(s->size >= 3 * sizeof(unsigned long)){
*((long *)(s->addr + 8)) =
(long)&start_debug_thread;
}
#ifdef __ppc__
if(s->size >= 5 * sizeof(unsigned long)){
images_dyld_stub_binding_helper =
*((long *)(s->addr + 20));
}
#endif
}
s++;
}
if((sg->initprot & VM_PROT_WRITE) == 0){
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if((s->flags & S_ATTR_EXT_RELOC)){
if((sg->maxprot & VM_PROT_READ) == 0 ||
(sg->maxprot & VM_PROT_WRITE) == 0){
error("malformed executable: %s (segment %.16s "
"has relocation entries but the max vm "
"protection does not allow reading and "
"writing)", name, sg->segname);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO,
name);
}
change_protect_on_reloc = TRUE;
break;
}
s++;
}
}
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if(((strcmp(s->segname, "__TEXT") == 0 &&
strcmp(s->sectname, "__text") == 0) ||
(s->flags & S_ATTR_SOME_INSTRUCTIONS)) &&
(s->flags & S_ATTR_EXT_RELOC)){
cache_sync_on_reloc = TRUE;
break;
}
s++;
}
if(init == NULL){
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if((s->flags & SECTION_TYPE) ==
S_MOD_INIT_FUNC_POINTERS){
init = s;
break;
}
s++;
}
}
if(term == NULL){
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if((s->flags & SECTION_TYPE) ==
S_MOD_TERM_FUNC_POINTERS){
term = s;
break;
}
s++;
}
}
if(has_coalesced_sections == FALSE){
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if((s->flags & SECTION_TYPE) == S_COALESCED){
has_coalesced_sections = TRUE;
break;
}
s++;
}
}
break;
case LC_SYMTAB:
if(st == NULL)
st = (struct symtab_command *)lc;
break;
case LC_DYSYMTAB:
if(dyst == NULL)
dyst = (struct dysymtab_command *)lc;
break;
case LC_UNIXTHREAD:
thread_command = (struct thread_command *)lc;
*entry_point = get_entry_point(thread_command);
break;
case LC_TWOLEVEL_HINTS:
if(hints_cmd == NULL)
hints_cmd = (struct twolevel_hints_command *)lc;
break;
default:
if((lc->cmd & LC_REQ_DYLD) == LC_REQ_DYLD){
error("can't launch executable: %s (unknown load command "
"%ld (0x%x) required for execution)", name, i,
(unsigned int)(lc->cmd));
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
}
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(st == NULL && dyst != NULL){
error("malformed executable: %s (dynamic symbol table command "
"but no standard symbol table command)", name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
dyst = NULL;
}
else if(linkedit_segment == NULL){
if(dyst != NULL){
error("malformed executable: %s (dynamic symbol table command"
"but no " SEG_LINKEDIT "segment)", name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
error("malformed executable: %s (symbol table command but no "
SEG_LINKEDIT "segment)", name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
}
else{
error("possible malformed executable: %s (no dynamic symbol "
"table command)", name);
link_edit_error(DYLD_WARNING, EBADMACHO, name);
error("possible malformed executable: %s (no " SEG_LINKEDIT
" segment)", name);
link_edit_error(DYLD_WARNING, EBADMACHO, name);
}
st = NULL;
dyst = NULL;
}
else{
if(dyst == NULL){
error("possible malformed executable: %s (no dynamic symbol "
"table command)", name);
link_edit_error(DYLD_WARNING, EBADMACHO, name);
}
else{
check_linkedit_info(name, "executable", linkedit_segment, st,
dyst, NULL);
}
}
object_image = new_object_image();
object_image->image.name = save_string(name);
executables_name = object_image->image.name;
object_image->image.vmaddr_slide = 0;
object_image->image.vmaddr_size = 0;
object_image->image.seg1addr = seg1addr;
object_image->image.mh = mh;
object_image->image.st = st;
object_image->image.dyst = dyst;
object_image->image.hints_cmd = hints_cmd;
object_image->image.linkedit_segment = linkedit_segment;
object_image->image.change_protect_on_reloc = change_protect_on_reloc;
object_image->image.cache_sync_on_reloc = cache_sync_on_reloc;
object_image->image.has_coalesced_sections = has_coalesced_sections;
object_image->image.init = init;
object_image->image.term = term;
#ifdef __ppc__
object_image->image.dyld_stub_binding_helper =
images_dyld_stub_binding_helper;
#endif
object_image->image.outer_image = object_image;
object_image->image.valid = TRUE;
SET_LINK_STATE(object_image->module, BEING_LINKED);
if(dyld_insert_libraries != NULL){
if(dyld_print_libraries == TRUE)
print("loading libraries for DYLD_INSERT_LIBRARIES=%s\n",
dyld_insert_libraries);
dylib_name = dyld_insert_libraries;
for(;;){
p = strchr(dylib_name, ':');
if(p != NULL)
*p = '\0';
if(dyld_prebind_debug != 0 && prebinding == TRUE)
print("dyld: %s: prebinding disabled due to "
"DYLD_INSERT_LIBRARIES being set\n",
executables_name);
prebinding = FALSE;
(void)load_library_image(NULL, dylib_name, FALSE, NULL);
if(p == NULL){
break;
}
else{
*p = ':';
dylib_name = p + 1;
}
}
}
if(dyld_print_libraries == TRUE)
print("loading libraries for image: %s\n",
object_image->image.name);
loading_executables_libraries = TRUE;
(void)load_images_libraries(mh, &(object_image->image));
loading_executables_libraries = FALSE;
load_dependent_libraries();
if(force_flat_namespace == FALSE){
if((object_image->image.mh->flags & MH_TWOLEVEL) == MH_TWOLEVEL &&
(object_image->image.hints_cmd != NULL))
object_image->image.image_can_use_hints = TRUE;
else
object_image->image.image_can_use_hints = FALSE;
}
if(dyld_hints_debug != 0)
printf("image_can_use_hints = %s for image: %s\n",
object_image->image.image_can_use_hints == TRUE ?
"TRUE" : "FALSE", object_image->image.name);
gdb_dyld_state_changed();
}
static
char *
try_framework_search(
char *dylib_name,
char *path,
char *suffix)
{
char *name, *new_dylib_name;
unsigned long size;
if(path == NULL){
return(NULL);
}
new_dylib_name = NULL;
if((name = get_framework_name(dylib_name, &size, FALSE)) != NULL){
new_dylib_name = search_for_name_in_path(name, path, suffix);
}
if(new_dylib_name == NULL){
if((name = get_framework_name(dylib_name, &size, TRUE)) != NULL){
new_dylib_name = search_for_name_in_path(name, path, suffix);
}
}
return new_dylib_name;
}
static
char *
try_library_search(
char *dylib_name,
char *path,
char *suffix)
{
char *name, *new_dylib_name;
if(path == NULL){
return(NULL);
}
new_dylib_name = NULL;
name = strrchr(dylib_name, '/');
if(name != NULL && name[1] != '\0')
name++;
else
name = dylib_name;
new_dylib_name = search_for_name_in_path(name, path, suffix);
return new_dylib_name;
}
static
char *
try_image_suffix_search(
char *dylib_name,
char *suffix)
{
char *new_dylib_name, *ext;
struct stat stat_buf;
long nmlen;
if(suffix == NULL){
return(NULL);
}
nmlen = strlen(dylib_name);
new_dylib_name = allocate(nmlen + strlen(suffix) + 1);
ext = strrchr(dylib_name, '.');
if(nmlen > 6 && ext != NULL && strcmp(ext, ".dylib") == 0){
strcpy(new_dylib_name, dylib_name);
new_dylib_name[nmlen - 6] = '\0';
strcat(new_dylib_name, suffix);
strcat(new_dylib_name, ".dylib");
}
else{
strcpy(new_dylib_name, dylib_name);
strcat(new_dylib_name, suffix);
}
if(stat(new_dylib_name, &stat_buf) == 0){
return(new_dylib_name);
}
free(new_dylib_name);
return(NULL);
}
static
char *
try_executable_search(
char *dylib_name,
char *suffix)
{
char *constructed_name, *new_dylib_name;
struct stat stat_buf;
constructed_name = NULL;
if(strncmp(dylib_name, "@executable_path/",
sizeof("@executable_path/")-1) == 0){
if(executables_path != NULL){
constructed_name =
construct_at_executable_path_library(dylib_name);
new_dylib_name = try_image_suffix_search(constructed_name, suffix);
if (new_dylib_name) {
free(constructed_name);
return(new_dylib_name);
}
if(stat(constructed_name, &stat_buf) == 0){
return(constructed_name);
}
free(constructed_name);
}
else{
error("can not get executable's path, so can't construct "
"path for library: %s", dylib_name);
link_edit_error(DYLD_WARNING, ENOENT, dylib_name);
}
}
return(NULL);
}
enum bool
load_library_image(
struct dylib_command *dl,
char *dylib_name,
enum bool force_searching,
struct image **image_pointer)
{
char *new_dylib_name, *constructed_name;
int fd, errnum, save_errno;
struct stat stat_buf;
unsigned long file_size;
char *file_addr;
kern_return_t r;
struct fat_header *fat_header;
struct fat_arch *fat_archs, *best_fat_arch;
struct mach_header *mh;
new_dylib_name = NULL;
constructed_name = NULL;
if(dl != NULL || force_searching == TRUE){
if(dl != NULL)
dylib_name = (char *)dl + dl->dylib.name.offset;
if(dyld_framework_path != NULL)
new_dylib_name = try_framework_search(dylib_name,
dyld_framework_path,
dyld_image_suffix);
if(new_dylib_name == NULL && dyld_library_path != NULL)
new_dylib_name = try_library_search(dylib_name,
dyld_library_path,
dyld_image_suffix);
if(new_dylib_name == NULL &&
strncmp(dylib_name, "@executable_path/",
sizeof("@executable_path/")-1) == 0){
constructed_name = try_executable_search(dylib_name,
dyld_image_suffix);
if(constructed_name != NULL)
dylib_name = constructed_name;
}
if(new_dylib_name == NULL &&
constructed_name == NULL &&
dyld_image_suffix != NULL)
new_dylib_name = try_image_suffix_search(dylib_name,
dyld_image_suffix);
if(new_dylib_name != NULL)
dylib_name = new_dylib_name;
}
if(is_library_loaded_by_name(dylib_name, dl, image_pointer) == TRUE){
if(new_dylib_name != NULL)
free(new_dylib_name);
if(constructed_name != NULL)
free(constructed_name);
if(dl == NULL)
free(dylib_name);
return(TRUE);
}
fd = open(dylib_name, O_RDONLY, 0);
if(fd == -1 &&
(dl != NULL || force_searching == TRUE) &&
new_dylib_name == NULL &&
constructed_name == NULL){
save_errno = errno;
new_dylib_name = try_framework_search(dylib_name,
dyld_fallback_framework_path,
dyld_image_suffix);
if(new_dylib_name == NULL){
new_dylib_name = try_library_search(dylib_name,
dyld_fallback_library_path,
dyld_image_suffix);
}
if(new_dylib_name == NULL){
if(default_fallback_framework_path == NULL){
default_fallback_framework_path = allocate(strlen(home) +
sizeof(DEFAULT_FALLBACK_FRAMEWORK_PATH));
strcpy(default_fallback_framework_path, home);
strcat(default_fallback_framework_path,
DEFAULT_FALLBACK_FRAMEWORK_PATH);
}
new_dylib_name = try_framework_search(
dylib_name,
default_fallback_framework_path,
dyld_image_suffix);
}
if(new_dylib_name == NULL){
if(default_fallback_library_path == NULL){
default_fallback_library_path = allocate(strlen(home) +
sizeof(DEFAULT_FALLBACK_LIBRARY_PATH));
strcpy(default_fallback_library_path, home);
strcat(default_fallback_library_path,
DEFAULT_FALLBACK_LIBRARY_PATH);
}
new_dylib_name = try_library_search(
dylib_name,
default_fallback_library_path,
dyld_image_suffix);
}
if(new_dylib_name != NULL) {
dylib_name = new_dylib_name;
fd = open(dylib_name, O_RDONLY, 0);
}
else {
errno = save_errno;
}
}
if(fd == -1){
errnum = errno;
if(constructed_name != NULL)
dylib_name = constructed_name;
system_error(errnum, "can't open library: %s ", dylib_name);
link_edit_error(DYLD_FILE_ACCESS, errnum, dylib_name);
if(new_dylib_name != NULL)
free(new_dylib_name);
if(constructed_name != NULL)
free(constructed_name);
return(FALSE);
}
if(dyld_print_libraries == TRUE)
print("loading library: %s\n", dylib_name);
if(fstat(fd, &stat_buf) == -1){
errnum = errno;
system_error(errnum, "can't stat library: %s", dylib_name);
link_edit_error(DYLD_FILE_ACCESS, errnum, dylib_name);
goto load_library_image_cleanup2;
}
if(is_library_loaded_by_stat(dylib_name, dl, &stat_buf, image_pointer)
== TRUE){
close(fd);
if(new_dylib_name != NULL)
free(new_dylib_name);
if(constructed_name != NULL)
free(constructed_name);
if(dl == NULL)
free(dylib_name);
return(TRUE);
}
file_size = stat_buf.st_size;
if(file_size == 0){
error("truncated or malformed library: %s (file is empty)",
dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, dylib_name);
goto load_library_image_cleanup2;
}
if((stat_buf.st_mode & S_IFMT) != S_IFREG){
error("file is not a regular file: %s (can't possibly be a "
"library)", dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, dylib_name);
goto load_library_image_cleanup2;
}
if((r = map_fd((int)fd, (vm_offset_t)0, (vm_offset_t *)&file_addr,
(boolean_t)TRUE, (vm_size_t)file_size)) != KERN_SUCCESS){
mach_error(r, "can't map library: %s", dylib_name);
link_edit_error(DYLD_MACH_RESOURCE_RECOVERABLE, r, dylib_name);
goto load_library_image_cleanup2;
}
if(sizeof(struct fat_header) > file_size){
error("truncated or malformed library: %s (file too small to be "
"a library)", dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, dylib_name);
goto load_library_image_cleanup1;
}
fat_header = (struct fat_header *)file_addr;
#ifdef __BIG_ENDIAN__
if(fat_header->magic == FAT_MAGIC)
#endif
#ifdef __LITTLE_ENDIAN__
if(fat_header->magic == SWAP_LONG(FAT_MAGIC))
#endif
{
#ifdef __LITTLE_ENDIAN__
swap_fat_header(fat_header, LITTLE_ENDIAN_BYTE_SEX);
#endif
if(sizeof(struct fat_header) + fat_header->nfat_arch *
sizeof(struct fat_arch) > file_size){
error("truncated or malformed library: %s (fat file's fat_arch "
"structs extend past the end of the file)", dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, dylib_name);
goto load_library_image_cleanup1;
}
fat_archs = (struct fat_arch *)(file_addr +
sizeof(struct fat_header));
#ifdef __LITTLE_ENDIAN__
swap_fat_arch(fat_archs, fat_header->nfat_arch,
LITTLE_ENDIAN_BYTE_SEX);
#endif
best_fat_arch = cpusubtype_findbestarch(host_basic_info.cpu_type,
host_basic_info.cpu_subtype,
fat_archs,
fat_header->nfat_arch);
if(best_fat_arch == NULL){
error("bad CPU type in library: %s", dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADARCH, dylib_name);
goto load_library_image_cleanup1;
}
if(sizeof(struct mach_header) > best_fat_arch->size){
error("truncated or malformed library: %s (file too small to "
"be a library)", dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, dylib_name);
goto load_library_image_cleanup1;
}
mh = (struct mach_header *)(file_addr + best_fat_arch->offset);
if(mh->magic != MH_MAGIC){
error("malformed library: %s (not a Mach-O file, bad magic "
"number)", dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, dylib_name);
goto load_library_image_cleanup1;
}
return(map_library_image(dl, dylib_name, fd, file_addr, file_size,
best_fat_arch->offset, best_fat_arch->size,
stat_buf.st_dev, stat_buf.st_ino,
image_pointer));
}
else{
if(sizeof(struct mach_header) > file_size){
error("truncated or malformed library: %s (file too small to "
"be a library)", dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, dylib_name);
goto load_library_image_cleanup1;
}
mh = (struct mach_header *)file_addr;
if(mh->magic == SWAP_LONG(MH_MAGIC)){
error("bad CPU type in library: %s", dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, dylib_name);
goto load_library_image_cleanup1;
}
if(mh->magic != MH_MAGIC){
error("malformed library: %s (not a Mach-O file, bad magic "
"number)", dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, dylib_name);
goto load_library_image_cleanup1;
}
return(map_library_image(dl, dylib_name, fd, file_addr, file_size,
0, file_size, stat_buf.st_dev,
stat_buf.st_ino, image_pointer));
}
load_library_image_cleanup1:
if((r = vm_deallocate(mach_task_self(), (vm_address_t)file_addr,
(vm_size_t)file_size)) != KERN_SUCCESS){
mach_error(r, "can't vm_deallocate map_fd memory for library: %s",
dylib_name);
link_edit_error(DYLD_MACH_RESOURCE, r, dylib_name);
}
load_library_image_cleanup2:
if(close(fd) == -1){
errnum = errno;
system_error(errnum, "can't close file descriptor for library: %s ",
dylib_name);
link_edit_error(DYLD_UNIX_RESOURCE, errnum, dylib_name);
}
if(new_dylib_name != NULL)
free(new_dylib_name);
if(constructed_name != NULL)
free(constructed_name);
return(FALSE);
}
static
char *
get_framework_name(
char *name,
unsigned long *short_name_size,
enum bool with_underbar_suffix)
{
char *foo, *a, *b, *c, *d, *suffix;
unsigned long l, s;
*short_name_size = 0;
a = strrchr(name, '/');
if(a == NULL)
return(NULL);
if(a == name)
return(NULL);
foo = a + 1;
l = strlen(foo);
*short_name_size = l;
if(with_underbar_suffix){
suffix = strrchr(foo, '_');
if(suffix != NULL){
s = strlen(suffix);
if(suffix == foo || s < 2)
suffix = NULL;
else{
l -= s;
*short_name_size = l;
}
}
}
b = look_back_for_slash(name, a);
if(b == NULL){
if(strncmp(name, foo, l) == 0 &&
strncmp(name + l, ".framework/", sizeof(".framework/")-1 ) == 0)
return(name);
else{
*short_name_size = 0;
return(NULL);
}
}
else{
if(strncmp(b+1, foo, l) == 0 &&
strncmp(b+1 + l, ".framework/", sizeof(".framework/")-1 ) == 0)
return(b+1);
}
if(b == name){
*short_name_size = 0;
return(NULL);
}
c = look_back_for_slash(name, b);
if(c == NULL ||
c == name ||
strncmp(c+1, "Versions/", sizeof("Versions/")-1) != 0){
*short_name_size = 0;
return(NULL);
}
d = look_back_for_slash(name, c);
if(d == NULL){
if(strncmp(name, foo, l) == 0 &&
strncmp(name + l, ".framework/", sizeof(".framework/")-1 ) == 0)
return(name);
else{
*short_name_size = 0;
return(NULL);
}
}
else{
if(strncmp(d+1, foo, l) == 0 &&
strncmp(d+1 + l, ".framework/", sizeof(".framework/")-1 ) == 0)
return(d+1);
else{
*short_name_size = 0;
return(NULL);
}
}
}
static
char *
get_library_name(
char *name,
unsigned long *short_name_size)
{
char *a, *b, *c;
*short_name_size = 0;
a = strrchr(name, '.');
if(a == NULL)
return(NULL);
if(a == name)
return(NULL);
if(strcmp(a, ".dylib") != 0)
return(NULL);
if(a - name >= 3 && a[-2] == '.')
a = a - 2;
b = look_back_for_slash(name, a);
if(b == name)
return(NULL);
if(b == NULL){
c = strchr(name, '_');
if(c != NULL && c != name){
*short_name_size = c - name;
}
else
*short_name_size = a - name;
return(name);
}
else{
c = strchr(b+1, '_');
if(c != NULL && c != b+1){
*short_name_size = c - (b+1);
}
else
*short_name_size = a - (b+1);
return(b+1);
}
}
static
char *
look_back_for_slash(
char *name,
char *p)
{
for(p = p - 1; p >= name; p--){
if(*p == '/')
return(p);
}
return(NULL);
}
static
char *
search_for_name_in_path(
char *name,
char *path,
char *suffix)
{
char *dylib_name, *new_dylib_name, *p;
struct stat stat_buf;
dylib_name = allocate(strlen(name) + strlen(path) + 2);
for(;;){
p = strchr(path, ':');
if(p != NULL)
*p = '\0';
else{
if(*path == '\0')
return(NULL);
}
if(*path == '\0')
goto next_path;
strcpy(dylib_name, path);
strcat(dylib_name, "/");
strcat(dylib_name, name);
new_dylib_name = try_image_suffix_search(dylib_name, suffix);
if(new_dylib_name != NULL){
free(dylib_name);
if(p != NULL)
*p = ':';
return(new_dylib_name);
}
if(stat(dylib_name, &stat_buf) == 0){
if(p != NULL)
*p = ':';
return(dylib_name);
}
if(p == NULL){
free(dylib_name);
return(NULL);
}
else{
next_path:
*p = ':';
path = p + 1;
}
}
return(NULL);
}
static
char *
construct_at_executable_path_library(
char *dylib_name)
{
unsigned long m, n;
char *p;
static char *resolvedname = NULL;
static unsigned long resolvedname_len = 0;
if(resolvedname == NULL){
resolvedname = allocate(MAXPATHLEN + 1);
p = realpath(executables_path, resolvedname);
if(p == NULL){
system_error(errno, "can't get realpath of executable: %s ",
executables_path);
link_edit_error(DYLD_FILE_ACCESS, errno, resolvedname);
free(resolvedname);
resolvedname = NULL;
p = strrchr(executables_path, '/');
m = (p - executables_path) + 1;
n = strlen(dylib_name + (sizeof("@executable_path/") - 1));
p = allocate(m + n + 1);
strncpy(p, executables_path, m);
strcpy(p + m,
dylib_name + (sizeof("@executable_path/") - 1));
return(p);
}
}
if(resolvedname_len == 0){
p = strrchr(resolvedname, '/');
resolvedname_len = (p - resolvedname) + 1;
}
n = strlen(dylib_name + (sizeof("@executable_path/") - 1));
p = allocate(resolvedname_len + n + 1);
strncpy(p, resolvedname, resolvedname_len);
strcpy(p + resolvedname_len,
dylib_name + (sizeof("@executable_path/") - 1));
return(p);
}
static
enum bool
map_library_image(
struct dylib_command *dl,
char *dylib_name,
int fd,
char *file_addr,
unsigned long file_size,
unsigned long library_offset,
unsigned long library_size,
dev_t dev,
ino_t ino,
struct image **image_pointer)
{
struct mach_header *mh;
int errnum;
kern_return_t r;
unsigned long low_addr, high_addr, slide_value, seg1addr,
segs_read_write_addr;
struct segment_command *linkedit_segment, *mach_header_segment;
struct dysymtab_command *dyst;
struct symtab_command *st;
struct dylib_command *dlid;
struct routines_command *rc;
struct twolevel_hints_command *hints_cmd;
enum bool change_protect_on_reloc, cache_sync_on_reloc,
has_coalesced_sections;
struct section *init, *term;
struct library_image *library_image;
struct dyld_event event;
char *name;
unsigned long images_dyld_stub_binding_helper;
#ifdef __ppc__
images_dyld_stub_binding_helper =
(unsigned long)(&unlinked_lazy_pointer_handler);
#endif
mh = (struct mach_header *)(file_addr + library_offset);
if(check_image(dylib_name, "library", library_size, mh,
&linkedit_segment, &mach_header_segment, &dyst, &st, &dlid, &rc,
&low_addr, &high_addr) == FALSE)
goto map_library_image_cleanup;
if(mh->filetype != MH_DYLIB){
error("malformed library: %s (not a Mach-O library file, bad "
"filetype value)", dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, dylib_name);
goto map_library_image_cleanup;
}
if(dlid == NULL){
error("malformed library: %s (Mach-O library file, does not have "
"an LC_ID_DYLIB command)", dylib_name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, dylib_name);
goto map_library_image_cleanup;
}
if(dl != NULL &&
dl->dylib.compatibility_version >
dlid->dylib.compatibility_version){
error("version mismatch for library: %s (compatibility "
"version of user: %lu.%lu.%lu greater than library's version: "
"%lu.%lu.%lu)", dylib_name,
dl->dylib.compatibility_version >> 16,
(dl->dylib.compatibility_version >> 8) & 0xff,
dl->dylib.compatibility_version & 0xff,
dlid->dylib.compatibility_version >> 16,
(dlid->dylib.compatibility_version >> 8) & 0xff,
dlid->dylib.compatibility_version & 0xff);
link_edit_error(DYLD_FILE_FORMAT, ESHLIBVERS, dylib_name);
goto map_library_image_cleanup;
}
if(map_image(dylib_name, "library", library_size, fd, file_addr,
file_size, library_offset, library_size, low_addr, high_addr, &mh,
&linkedit_segment, &dyst, &st, &dlid, &rc, &hints_cmd,
&change_protect_on_reloc, &cache_sync_on_reloc,
&has_coalesced_sections, &init, &term, &seg1addr,
&segs_read_write_addr, &slide_value,
&images_dyld_stub_binding_helper) == FALSE){
return(FALSE);
}
name = (char *)dlid + dlid->dylib.name.offset;
if(strcmp(dylib_name, name) == 0)
dylib_name = name;
library_image = new_library_image(dyst->nmodtab);
library_image->image.name = dylib_name;
library_image->image.vmaddr_slide = slide_value;
library_image->image.vmaddr_size = high_addr - low_addr;
library_image->image.seg1addr = seg1addr;
library_image->image.segs_read_write_addr = segs_read_write_addr;
library_image->image.mh = mh;
library_image->image.st = st;
library_image->image.dyst = dyst;
library_image->image.rc = rc;
library_image->image.hints_cmd = hints_cmd;
library_image->image.linkedit_segment = linkedit_segment;
library_image->image.change_protect_on_reloc = change_protect_on_reloc;
library_image->image.cache_sync_on_reloc = cache_sync_on_reloc;
library_image->image.has_coalesced_sections = has_coalesced_sections;
library_image->image.init = init;
library_image->image.term = term;
#ifdef __ppc__
library_image->image.dyld_stub_binding_helper =
images_dyld_stub_binding_helper;
#endif
library_image->dlid = dlid;
library_image->dev = dev;
library_image->ino = ino;
library_image->remove_on_error = return_on_error;
library_image->library_offset = library_offset;
library_image->image.sub_images_setup = FALSE;
library_image->image.umbrella_images_setup = FALSE;
if(image_pointer != NULL)
*image_pointer = &(library_image->image);
library_image->image.umbrella_name = get_framework_name(
(char *)dlid + dlid->dylib.name.offset,
&(library_image->image.name_size),
FALSE);
if(library_image->image.umbrella_name == NULL)
library_image->image.umbrella_name = get_framework_name(
(char *)dlid + dlid->dylib.name.offset,
&(library_image->image.name_size),
TRUE);
if(library_image->image.umbrella_name == NULL)
library_image->image.library_name = get_library_name(
(char *)dlid + dlid->dylib.name.offset,
&(library_image->image.name_size));
library_image->image.outer_image = library_image;
library_image->image.valid = TRUE;
if(slide_value != 0){
if(dyld_prebind_debug != 0 &&
prebinding == TRUE &&
launched == FALSE)
print("dyld: %s: prebinding disabled because library: %s got "
"slid\n", executables_name, dylib_name);
if(launched == FALSE)
prebinding = FALSE;
local_relocation(&(library_image->image));
relocate_symbol_pointers_for_defined_externs(
&(library_image->image));
}
if((mh->flags & MH_PREBOUND) != MH_PREBOUND){
if(dyld_prebind_debug != 0 &&
prebinding == TRUE &&
launched == FALSE)
print("dyld: %s: prebinding disabled because library: %s not "
"prebound\n", executables_name, dylib_name);
if(launched == FALSE)
prebinding = FALSE;
}
else{
if(force_flat_namespace == TRUE &&
(mh->flags & MH_TWOLEVEL) == MH_TWOLEVEL){
if(dyld_prebind_debug != 0 &&
prebinding == TRUE &&
launched == FALSE)
print("dyld: %s: prebinding disabled because library: %s "
"is two-level namespace and flat namespace is being "
"used\n", executables_name, dylib_name);
if(launched == FALSE)
prebinding = FALSE;
}
if(launched == TRUE)
undo_prebinding_for_library(library_image);
}
if(dl != NULL &&
dl->dylib.timestamp != dlid->dylib.timestamp){
if(prebinding == TRUE && launched == FALSE){
if(loading_executables_libraries == FALSE ||
executable_prebound == TRUE){
if(dyld_prebind_debug != 0)
print("dyld: %s: prebinding disabled because time "
"stamp of library: %s did not match\n",
executables_name, dylib_name);
prebinding = FALSE;
}
}
}
set_segment_protections(dylib_name, "library", mh, slide_value);
memset(&event, '\0', sizeof(struct dyld_event));
event.type = DYLD_IMAGE_ADDED;
event.arg[0].header = mh;
event.arg[0].vmaddr_slide = slide_value;
event.arg[0].module_index = 0;
send_event(&event);
return(TRUE);
map_library_image_cleanup:
if((r = vm_deallocate(mach_task_self(), (vm_address_t)file_addr,
(vm_size_t)file_size)) != KERN_SUCCESS){
mach_error(r, "can't vm_deallocate map_fd memory for library: %s",
dylib_name);
link_edit_error(DYLD_MACH_RESOURCE, r, dylib_name);
}
if(close(fd) == -1){
errnum = errno;
system_error(errnum, "can't close file descriptor for library: %s ",
dylib_name);
link_edit_error(DYLD_UNIX_RESOURCE, errnum, dylib_name);
}
return(FALSE);
}
struct object_image *
map_bundle_image(
char *name,
char *object_addr,
unsigned long object_size)
{
struct mach_header *mh;
unsigned long low_addr, high_addr, slide_value, seg1addr,
segs_read_write_addr;
struct segment_command *linkedit_segment, *mach_header_segment;
struct dysymtab_command *dyst;
struct symtab_command *st;
struct twolevel_hints_command *hints_cmd;
enum bool change_protect_on_reloc, cache_sync_on_reloc,
has_coalesced_sections;
struct section *init, *term;
struct object_image *object_image;
struct dyld_event event;
unsigned long images_dyld_stub_binding_helper;
#ifdef __ppc__
images_dyld_stub_binding_helper =
(unsigned long)(&unlinked_lazy_pointer_handler);
#endif
if(((int)object_addr % vm_page_size) != 0){
error("malformed object file image: %s (address of image not on a "
"page boundary)", name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(NULL);
}
if(sizeof(struct mach_header) > object_size){
error("truncated or malformed object file image: %s (too small to "
"be an object file image)", name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(NULL);
}
mh = (struct mach_header *)object_addr;
if(mh->magic != MH_MAGIC){
error("malformed object file image: %s (not a Mach-O image, bad "
"magic number)", name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(NULL);
}
if(check_image(name, "object file image", object_size, mh,
&linkedit_segment, &mach_header_segment, &dyst, &st, NULL, NULL,
&low_addr, &high_addr) == FALSE)
return(NULL);
if(mh->filetype != MH_BUNDLE){
error("malformed object file image: %s (not a Mach-O bundle file, "
"bad filetype value)", name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(NULL);
}
if(map_image(name, "object file image", object_size, -1, NULL,
0, 0, 0, low_addr, high_addr, &mh, &linkedit_segment, &dyst, &st,
NULL, NULL, &hints_cmd, &change_protect_on_reloc,
&cache_sync_on_reloc, &has_coalesced_sections, &init, &term,
&seg1addr, &segs_read_write_addr, &slide_value,
&images_dyld_stub_binding_helper) == FALSE){
return(NULL);
}
object_image = new_object_image();
object_image->image.name = save_string(name);
object_image->image.vmaddr_slide = slide_value;
object_image->image.vmaddr_size = high_addr - low_addr;
object_image->image.seg1addr = seg1addr;
object_image->image.segs_read_write_addr = segs_read_write_addr;
object_image->image.mh = mh;
object_image->image.st = st;
object_image->image.dyst = dyst;
object_image->image.hints_cmd = hints_cmd;
object_image->image.linkedit_segment = linkedit_segment;
object_image->image.change_protect_on_reloc = change_protect_on_reloc;
object_image->image.cache_sync_on_reloc = cache_sync_on_reloc;
object_image->image.has_coalesced_sections = has_coalesced_sections;
object_image->image.init = init;
object_image->image.term = term;
#ifdef __ppc__
object_image->image.dyld_stub_binding_helper =
images_dyld_stub_binding_helper;
#endif
object_image->image.outer_image = object_image;
object_image->image.valid = TRUE;
SET_LINK_STATE(object_image->module, UNLINKED);
if(slide_value != 0){
local_relocation(&(object_image->image));
relocate_symbol_pointers_for_defined_externs(
&(object_image->image));
}
set_segment_protections(name, "object file image", mh, slide_value);
memset(&event, '\0', sizeof(struct dyld_event));
event.type = DYLD_IMAGE_ADDED;
event.arg[0].header = mh;
event.arg[0].vmaddr_slide = slide_value;
event.arg[0].module_index = 0;
send_event(&event);
if(dyld_print_libraries == TRUE)
print("loading libraries for image: %s\n",
object_image->image.name);
if(load_images_libraries(mh, &(object_image->image)) == FALSE &&
return_on_error == TRUE){
object_image->image.private = TRUE;
unload_bundle_image(object_image, FALSE, FALSE);
return(NULL);
}
return(object_image);
}
void
unload_bundle_image(
struct object_image *object_image,
enum bool keepMemoryMapped,
enum bool reset_lazy_references)
{
kern_return_t r;
struct dyld_event event;
object_image->image.valid = FALSE;
memset(&event, '\0', sizeof(struct dyld_event));
event.type = DYLD_MODULE_REMOVED;
event.arg[0].header = object_image->image.mh;
event.arg[0].vmaddr_slide = object_image->image.vmaddr_slide;
event.arg[0].module_index = 0;
send_event(&event);
if(keepMemoryMapped == FALSE){
memset(&event, '\0', sizeof(struct dyld_event));
event.type = DYLD_IMAGE_REMOVED;
event.arg[0].header = object_image->image.mh;
event.arg[0].vmaddr_slide = object_image->image.vmaddr_slide;
event.arg[0].module_index = 0;
send_event(&event);
}
if(object_image->image.private == FALSE){
unlink_object_module(object_image, reset_lazy_references);
check_and_report_undefineds();
}
if(keepMemoryMapped == FALSE){
if((r = vm_deallocate(mach_task_self(),
(vm_address_t)object_image->image.mh,
(vm_size_t)object_image->image.vmaddr_size)) !=
KERN_SUCCESS){
mach_error(r, "can't vm_deallocate memory for module: %s",
object_image->image.name);
link_edit_error(DYLD_MACH_RESOURCE, r,object_image->image.name);
}
}
memset(object_image, '\0', sizeof(struct object_image));
SET_LINK_STATE(object_image->module, UNUSED);
return;
}
static
enum bool
map_image(
char *name,
char *image_type,
unsigned long image_size,
int fd,
char *file_addr,
unsigned long file_size,
unsigned long library_offset,
unsigned long library_size,
unsigned long low_addr,
unsigned long high_addr,
struct mach_header **mh,
struct segment_command **linkedit_segment,
struct dysymtab_command **dyst,
struct symtab_command **st,
struct dylib_command **dlid,
struct routines_command **rc,
struct twolevel_hints_command **hints_cmd,
enum bool *change_protect_on_reloc,
enum bool *cache_sync_on_reloc,
enum bool *has_coalesced_sections,
struct section **init,
struct section **term,
unsigned long *seg1addr,
unsigned long *segs_read_write_addr,
unsigned long *slide_value,
unsigned long *images_dyld_stub_binding_helper)
{
vm_address_t address, image_addr;
vm_size_t size;
#ifdef __MACH30__
vm_region_info_data_t info;
mach_msg_type_number_t infoCnt;
#else
vm_prot_t protection, max_protection;
vm_inherit_t inheritance;
boolean_t shared;
vm_offset_t offset;
#endif
mach_port_t object_name;
enum bool slide_it, in_the_way;
int errnum;
kern_return_t r;
unsigned long i, j;
struct load_command *lc, *load_commands;
struct segment_command *sg;
struct section *s;
unsigned long mach_header_segment_vmaddr;
#ifdef SHARED_LIBRARY_SERVER_SUPPORTED
unsigned long nsegs;
int ret;
#define ARRAY_ENTRIES 5
struct sf_mapping sf_mapping_array[ARRAY_ENTRIES], *sf_mapping_pointer, *m;
vm_address_t base_address;
int flags;
static enum bool first_load_shared_file = TRUE;
#endif
mach_header_segment_vmaddr = 0;
#ifdef SHARED_LIBRARY_SERVER_SUPPORTED
if((*mh)->flags & MH_SPLIT_SEGS){
nsegs = 0;
*seg1addr = ULONG_MAX;
*segs_read_write_addr = ULONG_MAX;
load_commands = (struct load_command *)((char *)*mh +
sizeof(struct mach_header));
lc = load_commands;
for(i = 0; i < (*mh)->ncmds; i++){
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
nsegs++;
if(sg->vmaddr < *seg1addr)
*seg1addr = sg->vmaddr;
if((sg->initprot & VM_PROT_WRITE) == VM_PROT_WRITE &&
sg->vmsize > sg->filesize)
nsegs++;
if((sg->initprot & VM_PROT_WRITE) == VM_PROT_WRITE &&
sg->vmaddr < *segs_read_write_addr)
*segs_read_write_addr = sg->vmaddr;
if(sg->fileoff == 0)
mach_header_segment_vmaddr = sg->vmaddr;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(nsegs <= ARRAY_ENTRIES){
sf_mapping_pointer = NULL;
m = sf_mapping_array;
}
else{
sf_mapping_pointer = allocate(sizeof(struct sf_mapping) *
nsegs);
m = sf_mapping_pointer;
}
j = 0;
lc = load_commands;
for(i = 0; i < (*mh)->ncmds; i++){
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
m[j].cksum = 0;
m[j].size = sg->filesize;
m[j].file_offset = sg->fileoff + library_offset;
m[j].mapping_offset = sg->vmaddr - *seg1addr;
if((sg->initprot & VM_PROT_WRITE) == VM_PROT_WRITE)
m[j].protection = sg->initprot | VM_PROT_COW;
else
m[j].protection = sg->initprot;
j++;
if((sg->initprot & VM_PROT_WRITE) == VM_PROT_WRITE &&
sg->vmsize > sg->filesize){
m[j].size = sg->vmsize - sg->filesize;
m[j].file_offset = 0;
m[j].mapping_offset = (sg->vmaddr + sg->filesize) -
*seg1addr;
m[j].protection = sg->initprot | VM_PROT_COW |
VM_PROT_ZF;
j++;
}
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
base_address = *seg1addr;
flags = 0;
#ifdef NEW_LOCAL_SHARED_REGIONS
if(first_load_shared_file == TRUE &&
dyld_new_local_shared_regions == TRUE)
flags |= NEW_LOCAL_SHARED_REGIONS;
#endif
ret = load_shared_file(name, (caddr_t)file_addr, file_size,
(caddr_t *)&base_address, nsegs, m, &flags);
if(ret == -1){
flags |= ALTERNATE_LOAD_SITE;
ret = load_shared_file(name, (caddr_t)file_addr, file_size,
(caddr_t *)&base_address, nsegs, m, &flags);
}
first_load_shared_file = FALSE;
if(ret == -1){
errnum = errno;
system_error(errnum, "load_shared_file() failed for %s ", name);
link_edit_error(DYLD_UNIX_RESOURCE, errnum, name);
if(sf_mapping_pointer != NULL)
free(sf_mapping_pointer);
goto map_image_cleanup1;
}
*slide_value = base_address - *seg1addr;
if(sf_mapping_pointer != NULL)
free(sf_mapping_pointer);
goto cleanup_and_reset_pointers;
}
#else
if((*mh)->flags & MH_SPLIT_SEGS){
error("unsupported Mach-O format file: %s (MH_SPILT_SEGS format "
"not supported in this version of dyld)", name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
#endif
slide_it = FALSE;
in_the_way = FALSE;
address = low_addr;
#ifdef __MACH30__
infoCnt = VM_REGION_BASIC_INFO_COUNT;
r = vm_region(mach_task_self(), &address, &size, VM_REGION_BASIC_INFO,
info, &infoCnt, &object_name);
#else
r = vm_region(mach_task_self(), &address, &size, &protection,
&max_protection, &inheritance, &shared, &object_name,
&offset);
#endif
if(r == KERN_SUCCESS){
if(address < high_addr){
if(fd != -1 && address == (vm_offset_t)file_addr){
in_the_way = TRUE;
address = (vm_offset_t)(file_addr + file_size);
#ifdef __MACH30__
infoCnt = VM_REGION_BASIC_INFO_COUNT;
r = vm_region(mach_task_self(), &address, &size,
VM_REGION_BASIC_INFO, info, &infoCnt,
&object_name);
#else
r = vm_region(mach_task_self(), &address, &size, &protection,
&max_protection, &inheritance, &shared,
&object_name, &offset);
#endif
if(r == KERN_SUCCESS && address < high_addr)
slide_it = TRUE;
}
else{
slide_it = TRUE;
}
}
}
if(slide_it == FALSE){
*slide_value = 0;
if(in_the_way == TRUE){
if((r = vm_deallocate(mach_task_self(), (vm_address_t)file_addr,
(vm_size_t)file_size)) != KERN_SUCCESS){
mach_error(r, "can't vm_deallocate map_fd memory for "
"%s: %s", image_type, name);
link_edit_error(DYLD_MACH_RESOURCE, r, name);
goto map_image_cleanup2;
}
}
if((r = vm_allocate(mach_task_self(), (vm_address_t *)&low_addr,
high_addr - low_addr, FALSE)) != KERN_SUCCESS){
slide_it = TRUE;
}
if(in_the_way == TRUE){
if((r = map_fd((int)fd, 0, (vm_offset_t *)&file_addr,
(boolean_t)TRUE, (vm_size_t)file_size)) != KERN_SUCCESS){
mach_error(r, "can't map %s: %s", image_type, name);
link_edit_error(DYLD_MACH_RESOURCE, r, name);
if(slide_it == FALSE){
if((r = vm_deallocate(mach_task_self(), (vm_address_t)
low_addr, (vm_size_t)(high_addr - low_addr))) !=
KERN_SUCCESS){
mach_error(r, "can't vm_deallocate memory to load "
"in %s: %s", image_type, name);
link_edit_error(DYLD_MACH_RESOURCE, r, name);
}
}
goto map_image_cleanup2;
}
}
}
if(slide_it == TRUE){
if((r = vm_allocate(mach_task_self(), &address,high_addr - low_addr,
TRUE)) != KERN_SUCCESS){
mach_error(r, "can't vm_allocate memory to load in %s: %s",
image_type, name);
link_edit_error(DYLD_MACH_RESOURCE_RECOVERABLE, r, name);
goto map_image_cleanup1;
}
*slide_value = address - low_addr;
}
load_commands = (struct load_command *)((char *)*mh +
sizeof(struct mach_header));
lc = load_commands;
for(i = 0; i < (*mh)->ncmds; i++){
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
address = sg->vmaddr + *slide_value;
if(fd != -1){
if((r = map_fd((int)fd, (vm_offset_t)sg->fileoff +
library_offset, &address, FALSE,
(vm_size_t)sg->filesize)) != KERN_SUCCESS){
mach_error(r, "can't map segment: %.16s for %s: %s",
sg->segname, image_type, name);
link_edit_error(DYLD_MACH_RESOURCE_RECOVERABLE, r,name);
goto map_image_cleanup0;
}
}
else{
image_addr = (vm_address_t)*mh;
if((r = vm_copy(mach_task_self(), image_addr + sg->fileoff,
round(sg->filesize, vm_page_size), address)) !=
KERN_SUCCESS){
mach_error(r, "can't vm_copy segment: %.16s for %s: %s",
sg->segname, image_type, name);
link_edit_error(DYLD_MACH_RESOURCE_RECOVERABLE, r,name);
goto map_image_cleanup0;
}
}
if(sg->fileoff == 0)
mach_header_segment_vmaddr = sg->vmaddr;
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
#ifdef SHARED_LIBRARY_SERVER_SUPPORTED
cleanup_and_reset_pointers:
#endif
if(fd != -1){
if((r = vm_deallocate(mach_task_self(), (vm_address_t)file_addr,
(vm_size_t)file_size)) != KERN_SUCCESS){
mach_error(r, "can't vm_deallocate map_fd memory for %s: %s",
image_type, name);
link_edit_error(DYLD_MACH_RESOURCE, r, name);
}
if(close(fd) == -1){
errnum = errno;
system_error(errnum, "can't close file descriptor for %s: %s ",
image_type, name);
link_edit_error(DYLD_UNIX_RESOURCE, errnum, name);
}
}
*mh = (struct mach_header *)((char *)mach_header_segment_vmaddr +
*slide_value);
load_commands = (struct load_command *)((char *)*mh +
sizeof(struct mach_header));
lc = load_commands;
*st = NULL;
*dyst = NULL;
*linkedit_segment = NULL;
if(dlid != NULL)
*dlid = NULL;
if(rc != NULL)
*rc = NULL;
if(hints_cmd != NULL)
*hints_cmd = NULL;
*seg1addr = ULONG_MAX;
*segs_read_write_addr = ULONG_MAX;
*change_protect_on_reloc = FALSE;
*cache_sync_on_reloc = FALSE;
*has_coalesced_sections = FALSE;
*init = NULL;
*term = NULL;
for(i = 0; i < (*mh)->ncmds; i++){
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
if(strcmp(sg->segname, SEG_LINKEDIT) == 0){
if(*linkedit_segment == NULL)
*linkedit_segment = sg;
}
if(sg->vmaddr < *seg1addr)
*seg1addr = sg->vmaddr;
if(((*mh)->flags & MH_SPLIT_SEGS) == MH_SPLIT_SEGS &&
(sg->initprot & VM_PROT_WRITE) == VM_PROT_WRITE &&
sg->vmaddr < *segs_read_write_addr)
*segs_read_write_addr = sg->vmaddr;
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if(strcmp(s->segname, "__DATA") == 0 &&
strcmp(s->sectname, "__dyld") == 0){
if(s->size >= sizeof(unsigned long)){
*((long *)(s->addr + *slide_value)) =
(long)&stub_binding_helper_interface;
}
if(s->size >= 2 * sizeof(unsigned long)){
*((long *)(s->addr + *slide_value + 4)) =
(long)&_dyld_func_lookup;
}
if(s->size >= 3 * sizeof(unsigned long)){
*((long *)(s->addr + *slide_value + 8)) =
(long)&start_debug_thread;
}
#ifdef __ppc__
if(s->size >= 5 * sizeof(unsigned long)){
*images_dyld_stub_binding_helper =
*((long *)(s->addr + *slide_value + 20)) +
*slide_value;
}
#endif
}
if(dyld_monaddition != NULL){
if(strcmp(s->segname, SEG_TEXT) == 0 &&
strcmp(s->sectname, SECT_TEXT) == 0){
if(s->size != 0){
release_lock();
dyld_monaddition(
(char *)(s->addr + *slide_value),
(char *)(s->addr + *slide_value + s->size));
set_lock();
}
}
}
#ifndef __MACH30__
else{
if(profile_server == TRUE &&
strcmp(s->segname, SEG_TEXT) == 0 &&
strcmp(s->sectname, SECT_TEXT) == 0)
shared_pcsample_buffer(name, s, *slide_value);
}
#endif
s++;
}
if((sg->initprot & VM_PROT_WRITE) == 0){
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if((s->flags & S_ATTR_EXT_RELOC)){
*change_protect_on_reloc = TRUE;
break;
}
s++;
}
}
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if(((strcmp(s->segname, "__TEXT") == 0 &&
strcmp(s->sectname, "__text") == 0) ||
(s->flags & S_ATTR_SOME_INSTRUCTIONS)) &&
(s->flags & S_ATTR_EXT_RELOC)){
*cache_sync_on_reloc = TRUE;
break;
}
s++;
}
if(*init == NULL){
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if((s->flags & SECTION_TYPE) ==
S_MOD_INIT_FUNC_POINTERS){
*init = s;
break;
}
s++;
}
}
if(*term == NULL){
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if((s->flags & SECTION_TYPE) ==
S_MOD_TERM_FUNC_POINTERS){
*term = s;
break;
}
s++;
}
}
if(*has_coalesced_sections == FALSE){
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if((s->flags & SECTION_TYPE) == S_COALESCED){
*has_coalesced_sections = TRUE;
break;
}
s++;
}
}
break;
case LC_SYMTAB:
if(*st == NULL)
*st = (struct symtab_command *)lc;
break;
case LC_DYSYMTAB:
if(*dyst == NULL)
*dyst = (struct dysymtab_command *)lc;
break;
case LC_ID_DYLIB:
if(dlid != NULL && *dlid == NULL)
*dlid = (struct dylib_command *)lc;
break;
case LC_ROUTINES:
if(rc != NULL && *rc == NULL)
*rc = (struct routines_command *)lc;
break;
case LC_TWOLEVEL_HINTS:
if(hints_cmd != NULL && *hints_cmd == NULL)
*hints_cmd = (struct twolevel_hints_command *)lc;
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
return(TRUE);
map_image_cleanup0:
if((r = vm_deallocate(mach_task_self(), (vm_address_t)low_addr,
(vm_size_t)(high_addr - low_addr))) != KERN_SUCCESS){
mach_error(r, "can't vm_deallocate memory to load in "
"%s: %s", image_type, name);
link_edit_error(DYLD_MACH_RESOURCE, r, name);
}
map_image_cleanup1:
if(fd != -1){
if((r = vm_deallocate(mach_task_self(), (vm_address_t)file_addr,
(vm_size_t)file_size)) != KERN_SUCCESS){
mach_error(r, "can't vm_deallocate map_fd memory for %s: %s",
image_type, name);
link_edit_error(DYLD_MACH_RESOURCE, r, name);
}
}
map_image_cleanup2:
if(fd != -1){
if(close(fd) == -1){
errnum = errno;
system_error(errnum, "can't close file descriptor for %s: %s ",
image_type, name);
link_edit_error(DYLD_UNIX_RESOURCE, errnum, name);
}
}
return(FALSE);
}
#ifndef __MACH30__
void
shared_pcsample_buffer(
char *name,
struct section *s,
unsigned long slide_value)
{
struct stat stat_buf;
unsigned long size, expected_size;
kern_return_t r;
char *buf, *rbuf;
int fd;
char gmon_out[MAXPATHLEN];
static enum bool first_time = TRUE;
#ifdef __OPENSTEP__
struct phdr profile_header;
#else
struct gmonhdr profile_header;
#endif
if(dyld_sample_debug == 2)
print("calling buffer_for_dylib for: %s\n", name);
if(buffer_for_dylib(name, gmon_out) == FALSE){
if(dyld_sample_debug == 2)
print("buffer_for_dylib for: %s returned FALSE\n", name);
return;
}
if(dyld_sample_debug == 2)
print("buffer_for_dylib for: %s returned: %s\n", name, gmon_out);
fd = open(gmon_out, O_RDWR, 0);
if(fd == -1){
if(dyld_sample_debug)
print("can't open: %s for: %s\n", gmon_out, name);
return;
}
if(fstat(fd, &stat_buf) == -1){
if(dyld_sample_debug)
print("can't stat: %s for: %s\n", gmon_out, name);
(void)close(fd);
return;
}
size = stat_buf.st_size;
expected_size = round(s->size / 1, sizeof(unsigned short)) +
sizeof(profile_header);
if(size != expected_size){
if(dyld_sample_debug)
print("size of: %s for: %s is %ld, expected %ld\n", gmon_out,
name, size, expected_size);
(void)close(fd);
return;
}
#ifndef MWATSON
r = vm_allocate(mach_task_self(), (vm_address_t *)&buf, size, TRUE);
if(r != KERN_SUCCESS){
if(dyld_sample_debug)
print("can't vm_allocate buffer to map: %s for: %s\n",
gmon_out, name);
(void)close(fd);
return;
}
rbuf = (char *)mmap(buf, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
#else
rbuf = (char *)mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
#endif
#ifndef __OPENSTEP__
if(rbuf == NULL)
#else
if((int)rbuf == -1)
#endif
{
if(dyld_sample_debug)
print("can't mmap: %s for: %s\n", gmon_out, name);
goto cleanup;
}
(void)close(fd);
if(first_time == TRUE){
if(profil(rbuf + sizeof(profile_header),
size - sizeof(profile_header),
(int)s->addr + slide_value,
SCALE_1_TO_1) == -1){
if(dyld_sample_debug)
print("profil failed: %s for: %s\n", gmon_out, name);
goto cleanup;
}
first_time = FALSE;
}
else{
if(add_profil(rbuf + sizeof(profile_header),
size - sizeof(profile_header),
(int)s->addr + slide_value,
SCALE_1_TO_1) == -1){
if(dyld_sample_debug)
print("profil failed: %s for: %s\n", gmon_out, name);
goto cleanup;
}
}
if(dyld_sample_debug == 2)
print("successfully set up: %s for: %s\n", gmon_out, name);
return;
cleanup:
(void)close(fd);
#ifndef MWATSON
r = vm_deallocate(mach_task_self(), (vm_address_t)buf, (vm_size_t)size);
if(r != KERN_SUCCESS){
mach_error(r, "can't vm_deallocate shared pcsample buffer "
" memory for %s", name);
link_edit_error(DYLD_MACH_RESOURCE, r, name);
}
#endif
return;
}
#endif
static
void
set_segment_protections(
char *name,
char *image_type,
struct mach_header *mh,
unsigned long slide_value)
{
unsigned long i;
struct load_command *lc, *load_commands;
struct segment_command *sg;
vm_address_t address;
kern_return_t r;
if(mh->flags & MH_SPLIT_SEGS)
return;
load_commands = (struct load_command *)((char *)mh +
sizeof(struct mach_header));
lc = load_commands;
for(i = 0; i < mh->ncmds; i++){
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
address = sg->vmaddr + slide_value;
if((r = vm_protect(mach_task_self(), address,
(vm_size_t)sg->vmsize,
FALSE, sg->initprot)) != KERN_SUCCESS){
mach_error(r, "can't vm_protect segment: %.16s for %s:"
" %s", sg->segname, image_type, name);
link_edit_error(DYLD_MACH_RESOURCE, r, name);
}
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
}
#define SANITY_CHECK_TWOLEVEL_PREBOUND
static
enum bool
all_dependents_twolevel_prebound(
struct library_image *const lib)
{
const struct dylib_command *dl;
const struct load_command *lc;
struct library_image *dep_lib;
struct image *dep_image;
const char *dep_name;
unsigned long i, j;
#ifdef SANITY_CHECK_TWOLEVEL_PREBOUND
if(lib->image.subtrees_twolevel_prebound_setup == TRUE){
print("dyld: internal error calc_twolevel_prebound_dependency() "
"called for: %s who's two-level prebound state is already "
"setup", lib->image.name);
return(lib->image.subtrees_twolevel_prebound);
}
#endif
lib->image.subtrees_twolevel_prebound_setup = TRUE;
lib->image.subtrees_twolevel_prebound = FALSE;
if(dyld_prebind_debug > 2)
print("dyld: checking: %s and dependents\n", lib->image.name);
lc = (struct load_command *)
((char *)lib->image.mh + sizeof(struct mach_header));
j = 0;
for(i = 0; i < lib->image.mh->ncmds; i++){
if(lc->cmd == LC_LOAD_DYLIB){
dl = (struct dylib_command *)lc;
dep_name = (char *)dl + dl->dylib.name.offset;
dep_image = lib->image.dependent_images[j++];
dep_lib = (struct library_image *)(dep_image->outer_image);
if(dl->dylib.timestamp != dep_lib->dlid->dylib.timestamp){
if(dyld_prebind_debug > 2)
print("dyld: %s timestamp differs from the "
"load_cmd's\n", lib->image.name);
return(FALSE);
}
else if(dep_image->subtrees_twolevel_prebound_setup){
if(dep_image->subtrees_twolevel_prebound == FALSE)
return(FALSE);
}
else{
dep_image->subtrees_twolevel_prebound =
all_dependents_twolevel_prebound(dep_lib);
if(dep_image->subtrees_twolevel_prebound == FALSE)
return(FALSE);
}
#ifdef SANITY_CHECK_TWOLEVEL_PREBOUND
if(dep_image->subtrees_twolevel_prebound == FALSE)
print("dyld: internal error: lib %s twolevel_prebound state"
" is FALSE!", dep_image->name);
#endif
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
lib->image.subtrees_twolevel_prebound = TRUE;
if(dyld_prebind_debug > 2)
print(" --> %s and its dependents are OK\n", lib->image.name);
return(TRUE);
}
void
find_twolevel_prebound_lib_subtrees(
void)
{
struct library_images *q;
struct library_image *l;
unsigned long i, total, used;
if(dyld_prebind_debug > 2)
print("dyld: In find_twolevel_prebound_lib_subtrees()\n");
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
l = &q->images[i];
if((l->image.mh->flags & (MH_TWOLEVEL | MH_PREBOUND)) !=
(MH_TWOLEVEL | MH_PREBOUND) ||
l->image.vmaddr_slide != 0){
l->image.subtrees_twolevel_prebound = FALSE;
l->image.subtrees_twolevel_prebound_setup = TRUE;
if(dyld_prebind_debug > 2){
print("dyld: **** '%s' ", l->image.name);
if(l->image.vmaddr_slide != 0)
print("slid ****\n");
else
print("not twolevel-prebound ****\n");
}
}
}
q = q->next_images;
}while(q != NULL);
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
l = &q->images[i];
if(l->image.subtrees_twolevel_prebound_setup == FALSE){
l->image.subtrees_twolevel_prebound =
all_dependents_twolevel_prebound(l);
}
}
q = q->next_images;
}while(q != NULL);
if(dyld_prebind_debug != 0){
total = 0;
used = 0;
q = &library_images;
do{
for(i = 0; i < q->nimages; ++i){
l = &q->images[i];
#ifdef SANITY_CHECK_TWOLEVEL_PREBOUND
if(l->image.subtrees_twolevel_prebound_setup == FALSE){
print("dyld: internal error two-level prebound state "
"of lib: %s not setup\n", l->image.name);
}
#endif
total++;
if(l->image.subtrees_twolevel_prebound == TRUE){
used++;
if(dyld_prebind_debug > 1)
print("dyld: using: %s as two-level prebound\n",
q->images[i].image.name);
}
else{
if(dyld_prebind_debug > 1)
print("dyld: NOT using: %s as two-level prebound\n",
q->images[i].image.name);
}
}
q = q->next_images;
}while(q != NULL);
print("dyld: %lu two-level prebound libraries used out of %ld\n",
used, total);
}
}
enum bool
load_dependent_libraries(
void)
{
unsigned long i, max_libraries;
struct library_images *q;
enum bool some_images_setup;
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
if(q->images[i].dependent_libraries_loaded == FALSE){
if(dyld_print_libraries == TRUE)
print("loading libraries for image: %s\n",
q->images[i].image.name);
if(load_images_libraries(q->images[i].image.mh,
&(q->images[i].image)) == FALSE &&
return_on_error == TRUE)
return(FALSE);
q->images[i].dependent_libraries_loaded = TRUE;
}
}
q = q->next_images;
}while(q != NULL);
if(force_flat_namespace == FALSE){
do{
some_images_setup = FALSE;
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
if(q->images[i].image.sub_images_setup == FALSE){
some_images_setup |=
setup_sub_images(&(q->images[i]));
}
}
q = q->next_images;
}while(q != NULL);
}while(some_images_setup == TRUE);
max_libraries = 0;
q = &library_images;
do{
max_libraries += q->nimages;
q = q->next_images;
}while(q != NULL);
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
if(q->images[i].image.umbrella_images_setup == FALSE){
setup_umbrella_images(&(q->images[i]), max_libraries);
}
}
q = q->next_images;
}while(q != NULL);
if(dyld_two_level_debug == TRUE){
static enum bool list_printed = FALSE;
if(list_printed == FALSE){
printf("Library order\n");
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
if(q->images[i].image.umbrella_name != NULL)
printf("\t%.*s\n",
(int)q->images[i].image.name_size,
q->images[i].image.umbrella_name);
else if(q->images[i].image.library_name != NULL)
printf("\t%.*s\n",
(int)q->images[i].image.name_size,
q->images[i].image.library_name);
else
printf("\t%s\n", q->images[i].image.name);
}
q = q->next_images;
}while(q != NULL);
list_printed = TRUE;
}
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
unsigned long j;
struct image **sp;
if(q->images[i].image.two_level_debug_printed == FALSE){
printf("library: %s",
q->images[i].image.name);
if(q->images[i].image.umbrella_name != NULL)
printf(" umbrella_name = %.*s\n",
(int)(q->images[i].image.name_size),
q->images[i].image.umbrella_name);
else if(q->images[i].image.library_name != NULL)
printf(" library_name = %.*s\n",
(int)(q->images[i].image.name_size),
q->images[i].image.library_name);
else
printf(" umbrella & library name = NULL\n");
printf(" ndependent_images = %lu\n",
q->images[i].image.ndependent_images);
sp = q->images[i].image.dependent_images;
for(j = 0;
j < q->images[i].image.ndependent_images;
j++){
if(sp[j]->umbrella_name != NULL)
printf("\t[%lu] %.*s\n", j,
(int)sp[j]->name_size,
sp[j]->umbrella_name);
else if(sp[j]->library_name != NULL)
printf("\t[%lu] %.*s\n", j,
(int)sp[j]->name_size,
sp[j]->library_name);
else
printf("\t[%lu] %s\n", j, sp[j]->name);
}
printf(" nsub_images = %lu\n",
q->images[i].image.nsub_images);
sp = q->images[i].image.sub_images;
for(j = 0; j < q->images[i].image.nsub_images; j++){
if(sp[j]->umbrella_name != NULL)
printf("\t[%lu] %.*s\n", j,
(int)sp[j]->name_size,
sp[j]->umbrella_name);
else if(sp[j]->library_name != NULL)
printf("\t[%lu] %.*s\n", j,
(int)sp[j]->name_size,
sp[j]->library_name);
else
printf("\t[%lu] %s\n", j, sp[j]->name);
}
printf(" numbrella_images = %lu\n",
q->images[i].image.numbrella_images);
sp = q->images[i].image.umbrella_images;
for(j = 0;
j < q->images[i].image.numbrella_images;
j++){
if(sp[j]->umbrella_name != NULL)
printf("\t[%lu] %.*s\n", j,
(int)sp[j]->name_size,
sp[j]->umbrella_name);
else if(sp[j]->library_name != NULL)
printf("\t[%lu] %.*s\n", j,
(int)sp[j]->name_size,
sp[j]->library_name);
else
printf("\t[%lu] %s\n", j, sp[j]->name);
}
}
q->images[i].image.two_level_debug_printed = TRUE;
}
q = q->next_images;
}while(q != NULL);
}
}
return(TRUE);
}
static
enum bool
load_images_libraries(
struct mach_header *mh,
struct image *image)
{
unsigned long i, ndependent_images;
struct load_command *lc, *load_commands;
struct dylib_command *dl_load;
struct image **dependent_images, **image_pointer;
load_commands = (struct load_command *)((char *)mh +
sizeof(struct mach_header));
ndependent_images = 0;
dependent_images = NULL;
if(force_flat_namespace == FALSE){
lc = load_commands;
for(i = 0; i < mh->ncmds; i++){
switch(lc->cmd){
case LC_LOAD_DYLIB:
ndependent_images++;
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
dependent_images = allocate_image_pointers(ndependent_images);
image->dependent_images = dependent_images;
image->ndependent_images = ndependent_images;
}
ndependent_images = 0;
lc = load_commands;
for(i = 0; i < mh->ncmds; i++){
switch(lc->cmd){
case LC_LOAD_DYLIB:
dl_load = (struct dylib_command *)lc;
if(dependent_images != NULL)
image_pointer = &(dependent_images[ndependent_images++]);
else
image_pointer = NULL;
if(load_library_image(dl_load, NULL, FALSE, image_pointer) ==
FALSE &&
return_on_error == TRUE){
unload_remove_on_error_libraries();
return(FALSE);
}
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
return(TRUE);
}
static
enum bool
setup_sub_images(
struct library_image *library_image)
{
unsigned long i, j, k, l, n, max_libraries;
struct mach_header *mh;
struct load_command *lc, *load_commands;
struct sub_umbrella_command *usub;
struct sub_library_command *lsub;
struct sub_framework_command *sub;
struct image **deps;
char *sub_umbrella_name, *sub_library_name, *sub_framework_name;
enum bool found, ignore_time_stamps, subs_can_use_hints, time_stamps_match;
max_libraries = 0;
deps = library_image->image.dependent_images;
subs_can_use_hints = TRUE;
mh = library_image->image.mh;
load_commands = (struct load_command *)((char *)mh +
sizeof(struct mach_header));
lc = load_commands;
for(i = 0; i < mh->ncmds; i++){
switch(lc->cmd){
case LC_SUB_UMBRELLA:
usub = (struct sub_umbrella_command *)lc;
sub_umbrella_name = (char *)usub + usub->sub_umbrella.offset;
for(j = 0; j < library_image->image.ndependent_images; j++){
if(deps[j]->umbrella_name != NULL &&
strncmp(sub_umbrella_name, deps[j]->umbrella_name,
deps[j]->name_size) == 0 &&
sub_umbrella_name[deps[j]->name_size] == '\0'){
if(deps[j]->sub_images_setup == FALSE)
return(FALSE);
max_libraries += 1 + deps[j]->nsub_images;
if(deps[j]->subs_can_use_hints == FALSE)
subs_can_use_hints = FALSE;
}
}
break;
case LC_SUB_LIBRARY:
lsub = (struct sub_library_command *)lc;
sub_library_name = (char *)lsub + lsub->sub_library.offset;
for(j = 0; j < library_image->image.ndependent_images; j++){
if(deps[j]->library_name != NULL &&
strncmp(sub_library_name, deps[j]->library_name,
deps[j]->name_size) == 0 &&
sub_library_name[deps[j]->name_size] == '\0'){
if(deps[j]->sub_images_setup == FALSE)
return(FALSE);
max_libraries += 1 + deps[j]->nsub_images;
if(deps[j]->subs_can_use_hints == FALSE)
subs_can_use_hints = FALSE;
}
}
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
max_libraries += library_image->image.ndependent_images;
library_image->image.sub_images =
allocate_image_pointers(max_libraries);
n = 0;
if((library_image->image.mh->flags & MH_NOMULTIDEFS) == MH_NOMULTIDEFS){
ignore_time_stamps = TRUE;
time_stamps_match = TRUE;
}
else{
ignore_time_stamps = FALSE;
if(subs_can_use_hints == TRUE)
time_stamps_match = TRUE;
else
time_stamps_match = FALSE;
}
if(library_image->image.umbrella_name != NULL){
for(i = 0; i < library_image->image.ndependent_images; i++){
mh = deps[i]->mh;
load_commands = (struct load_command *)((char *)(mh) +
sizeof(struct mach_header));
lc = load_commands;
for(j = 0; j < mh->ncmds; j++){
if(lc->cmd == LC_SUB_FRAMEWORK){
sub = (struct sub_framework_command *)lc;
sub_framework_name = (char *)sub + sub->umbrella.offset;
if(library_image->image.umbrella_name != NULL &&
strncmp(sub_framework_name,
library_image->image.umbrella_name,
library_image->image.name_size) == 0 &&
sub_framework_name[
library_image->image.name_size] =='\0'){
library_image->image.sub_images[n++] = deps[i];
deps[i]->umbrella_images_setup = FALSE;
if(ignore_time_stamps == FALSE &&
time_stamps_match == TRUE)
time_stamps_match = check_time_stamp(
library_image, deps[i]);
goto next_dep;
}
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
next_dep: ;
}
}
mh = library_image->image.mh;
load_commands = (struct load_command *)((char *)mh +
sizeof(struct mach_header));
lc = load_commands;
for(i = 0; i < mh->ncmds; i++){
switch(lc->cmd){
case LC_SUB_UMBRELLA:
usub = (struct sub_umbrella_command *)lc;
sub_umbrella_name = (char *)usub + usub->sub_umbrella.offset;
for(j = 0; j < library_image->image.ndependent_images; j++){
if(deps[j]->umbrella_name != NULL &&
strncmp(sub_umbrella_name, deps[j]->umbrella_name,
deps[j]->name_size) == 0 &&
sub_umbrella_name[deps[j]->name_size] == '\0'){
found = FALSE;
for(l = 0; l < n; l++){
if(library_image->image.sub_images[l] == deps[j]){
found = TRUE;
break;
}
}
if(found == FALSE){
library_image->image.sub_images[n++] = deps[j];
deps[j]->umbrella_images_setup = FALSE;
if(ignore_time_stamps == FALSE &&
time_stamps_match == TRUE)
time_stamps_match = check_time_stamp(
library_image, deps[j]);
}
for(k = 0; k < deps[j]->nsub_images; k++){
found = FALSE;
for(l = 0; l < n; l++){
if(library_image->image.sub_images[l] ==
deps[j]->sub_images[k]){
found = TRUE;
break;
}
}
if(found == FALSE){
library_image->image.sub_images[n++] =
deps[j]->sub_images[k];
deps[j]->sub_images[k]->umbrella_images_setup =
FALSE;
if(ignore_time_stamps == FALSE &&
time_stamps_match == TRUE)
time_stamps_match = check_time_stamp(
library_image, deps[j]->sub_images[k]);
}
}
}
}
break;
case LC_SUB_LIBRARY:
lsub = (struct sub_library_command *)lc;
sub_library_name = (char *)lsub + lsub->sub_library.offset;
for(j = 0; j < library_image->image.ndependent_images; j++){
if(deps[j]->library_name != NULL &&
strncmp(sub_library_name, deps[j]->library_name,
deps[j]->name_size) == 0 &&
sub_library_name[deps[j]->name_size] == '\0'){
found = FALSE;
for(l = 0; l < n; l++){
if(library_image->image.sub_images[l] == deps[j]){
found = TRUE;
break;
}
}
if(found == FALSE){
library_image->image.sub_images[n++] = deps[j];
deps[j]->umbrella_images_setup = FALSE;
if(ignore_time_stamps == FALSE &&
time_stamps_match == TRUE)
time_stamps_match = check_time_stamp(
library_image, deps[j]);
}
for(k = 0; k < deps[j]->nsub_images; k++){
found = FALSE;
for(l = 0; l < n; l++){
if(library_image->image.sub_images[l] ==
deps[j]->sub_images[k]){
found = TRUE;
break;
}
}
if(found == FALSE){
library_image->image.sub_images[n++] =
deps[j]->sub_images[k];
deps[j]->sub_images[k]->umbrella_images_setup =
FALSE;
if(ignore_time_stamps == FALSE &&
time_stamps_match == TRUE)
time_stamps_match = check_time_stamp(
library_image, deps[j]->sub_images[k]);
}
}
}
}
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
reallocate_image_pointers(library_image->image.sub_images,
max_libraries, n);
library_image->image.nsub_images = n;
library_image->image.sub_images_setup = TRUE;
if(subs_can_use_hints == TRUE){
if((library_image->image.mh->flags & MH_TWOLEVEL) == MH_TWOLEVEL &&
(library_image->image.hints_cmd != NULL))
library_image->image.image_can_use_hints = TRUE;
else
library_image->image.image_can_use_hints = FALSE;
if(time_stamps_match == TRUE)
library_image->image.subs_can_use_hints = TRUE;
else
library_image->image.subs_can_use_hints = FALSE;
}
else{
library_image->image.image_can_use_hints = FALSE;
library_image->image.subs_can_use_hints = FALSE;
}
if(dyld_hints_debug != 0)
printf("image_can_use_hints = %s subs_can_use_hints = %s for "
"image: %s\n",
library_image->image.image_can_use_hints == TRUE ?
"TRUE" : "FALSE",
library_image->image.subs_can_use_hints == TRUE ?
"TRUE" : "FALSE",
library_image->image.name);
return(TRUE);
}
static
enum bool
check_time_stamp(
struct library_image *library_image,
struct image *sub_image)
{
unsigned long i;
struct load_command *lc;
struct dylib_command *dl_load, *dlid;
char *dep_name, *name;
dlid = ((struct library_image *)sub_image->outer_image)->dlid;
dep_name = (char *)dlid + dlid->dylib.name.offset;
lc = (struct load_command *)((char *)(library_image->image.mh) +
sizeof(struct mach_header));
for(i = 0; i < library_image->image.mh->ncmds; i++){
switch(lc->cmd){
case LC_LOAD_DYLIB:
dl_load = (struct dylib_command *)lc;
name = (char *)dl_load + dl_load->dylib.name.offset;
if(strcmp(name, dep_name) == 0){
if(dlid->dylib.timestamp == dl_load->dylib.timestamp)
return(TRUE);
else
return(FALSE);
}
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
return(FALSE);
}
static
void
setup_umbrella_images(
struct library_image *library_image,
unsigned long max_libraries)
{
unsigned long i, j;
struct library_images *q;
enum bool found;
library_image->image.umbrella_images =
allocate_image_pointers(max_libraries);
library_image->image.numbrella_images = 0;
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
found = FALSE;
for(j = 0; j < library_image->image.numbrella_images; j++){
if(library_image->image.umbrella_images[j] ==
&(q->images[i].image) ){
found = TRUE;
break;
}
}
if(found == TRUE)
continue;
for(j = 0; j < q->images[i].image.nsub_images; j++){
if(q->images[i].image.sub_images[j] ==
&(library_image->image) ){
library_image->image.umbrella_images[
library_image->image.numbrella_images++] =
&(q->images[i].image);
break;
}
}
}
q = q->next_images;
}while(q != NULL);
reallocate_image_pointers(library_image->image.umbrella_images,
max_libraries,
library_image->image.numbrella_images);
library_image->image.umbrella_images_setup = TRUE;
}
void
unload_remove_on_error_libraries(
void)
{
struct library_images *p, *q, *temp;
struct library_image *library_image;
unsigned long i, j;
kern_return_t r;
enum bool split_lib;
struct dyld_event event;
for(p = &library_images; p != NULL; p = p->next_images){
for(i = 0; i < p->nimages; i++){
if(p->images[i].remove_on_error == TRUE){
for(j = i; j < p->nimages; j++){
library_image = p->images + j;
library_image->image.valid = FALSE;
memset(&event, '\0', sizeof(struct dyld_event));
event.type = DYLD_IMAGE_REMOVED;
event.arg[0].header = library_image->image.mh;
event.arg[0].vmaddr_slide =
library_image->image.vmaddr_slide;
event.arg[0].module_index = 0;
send_event(&event);
split_lib = (library_image->image.mh->flags &
MH_SPLIT_SEGS) == MH_SPLIT_SEGS;
if(split_lib == TRUE){
unload_shared_file(library_image);
}
else if((r = vm_deallocate(mach_task_self(),
(vm_address_t)library_image->image.mh,
(vm_size_t)library_image->image.vmaddr_size)) !=
KERN_SUCCESS){
mach_error(r, "can't vm_deallocate memory for "
"library: %s",library_image->image.name);
link_edit_error(DYLD_MACH_RESOURCE, r,
library_image->image.name);
}
deallocate_module_states(library_image->modules,
library_image->nmodules);
deallocate_image_pointers(
library_image->image.dependent_images,
library_image->image.ndependent_images);
deallocate_image_pointers(
library_image->image.sub_images,
library_image->image.nsub_images);
deallocate_image_pointers(
library_image->image.umbrella_images,
library_image->image.numbrella_images);
memset(library_image,'\0',sizeof(struct library_image));
}
p->nimages = i;
q = p->next_images;
p->next_images = NULL;
while(q != NULL){
for(j = 0; j < q->nimages; j++){
library_image = q->images + j;
library_image->image.valid = FALSE;
memset(&event, '\0', sizeof(struct dyld_event));
event.type = DYLD_IMAGE_REMOVED;
event.arg[0].header = library_image->image.mh;
event.arg[0].vmaddr_slide =
library_image->image.vmaddr_slide;
event.arg[0].module_index = 0;
send_event(&event);
split_lib = (library_image->image.mh->flags &
MH_SPLIT_SEGS) == MH_SPLIT_SEGS;
if(split_lib == TRUE){
unload_shared_file(library_image);
}
else if((r = vm_deallocate(mach_task_self(),
(vm_address_t)library_image->image.mh,
(vm_size_t)library_image->image.vmaddr_size)) !=
KERN_SUCCESS){
mach_error(r, "can't vm_deallocate memory for "
"library: %s", library_image->image.name);
link_edit_error(DYLD_MACH_RESOURCE, r,
library_image->image.name);
}
deallocate_module_states(library_image->modules,
library_image->nmodules);
deallocate_image_pointers(
library_image->image.dependent_images,
library_image->image.ndependent_images);
deallocate_image_pointers(
library_image->image.sub_images,
library_image->image.nsub_images);
deallocate_image_pointers(
library_image->image.umbrella_images,
library_image->image.numbrella_images);
}
temp = q->next_images;
free(q);
q = temp;
}
return;
}
}
}
}
static
void
unload_shared_file(
struct library_image *library_image)
{
#ifdef SHARED_LIBRARY_SERVER_SUPPORTED
unsigned long i, j;
struct load_command *lc, *load_commands;
struct segment_command *sg;
unsigned long nsegs;
int ret;
#define ARRAY_ENTRIES 5
struct sf_mapping sf_mapping_array[ARRAY_ENTRIES], *sf_mapping_pointer, *m;
vm_address_t base_address;
nsegs = 0;
load_commands = (struct load_command *)
((char *)(library_image->image.mh) +
sizeof(struct mach_header));
lc = load_commands;
for(i = 0; i < library_image->image.mh->ncmds; i++){
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
nsegs++;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(nsegs <= ARRAY_ENTRIES){
sf_mapping_pointer = NULL;
m = sf_mapping_array;
}
else{
sf_mapping_pointer = allocate(sizeof(struct sf_mapping) *
nsegs);
m = sf_mapping_pointer;
}
j = 0;
lc = load_commands;
for(i = 0; i < library_image->image.mh->ncmds; i++){
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
m[j].cksum = 0;
m[j].size = sg->filesize;
m[j].file_offset = sg->fileoff + library_image->library_offset;
m[j].mapping_offset = sg->vmaddr -
library_image->image.seg1addr;
if((sg->initprot & VM_PROT_WRITE) == VM_PROT_WRITE)
m[j].protection = sg->initprot | VM_PROT_COW;
else
m[j].protection = sg->initprot;
j++;
if((sg->initprot & VM_PROT_WRITE) == VM_PROT_WRITE &&
sg->vmsize > sg->filesize){
m[j].size = sg->vmsize - sg->filesize;
m[j].file_offset = 0;
m[j].mapping_offset = (sg->vmaddr + sg->filesize) -
library_image->image.seg1addr;
m[j].protection = sg->initprot | VM_PROT_COW |
VM_PROT_ZF;
j++;
}
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
base_address = library_image->image.seg1addr +
library_image->image.vmaddr_slide;
ret = reset_shared_file((caddr_t *)&base_address, nsegs, m);
if(ret == -1){
system_error(errno, "reset_shared_file() failed for %s ",
library_image->image.name);
link_edit_error(DYLD_UNIX_RESOURCE, errno,
library_image->image.name);
}
if(sf_mapping_pointer != NULL)
free(sf_mapping_pointer);
#endif
}
void
clear_remove_on_error_libraries(
void)
{
unsigned long i;
struct library_images *p;
for(p = &library_images; p != NULL; p = p->next_images){
for(i = 0; i < p->nimages; i++){
p->images[i].remove_on_error = FALSE;
}
}
}
static
enum bool
check_image(
char *name,
char *image_type,
unsigned long image_size,
struct mach_header *mh,
struct segment_command **linkedit_segment,
struct segment_command **mach_header_segment,
struct dysymtab_command **dyst,
struct symtab_command **st,
struct dylib_command **dlid,
struct routines_command **rc,
unsigned long *low_addr,
unsigned long *high_addr)
{
unsigned long i, j;
struct load_command *lc, *load_commands;
struct segment_command *sg;
struct dylib_command *dl_load;
char *load_dylib_name;
*linkedit_segment = NULL;
*mach_header_segment = NULL;
*st = NULL;
*dyst = NULL;
if(rc != NULL)
*rc = NULL;
if(dlid != NULL)
*dlid = NULL;
*low_addr = ULONG_MAX;
*high_addr = 0;
if(mh->cputype != host_basic_info.cpu_type){
error("bad CPU type in %s: %s", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADARCH, name);
return(FALSE);
}
if(cpusubtype_combine(host_basic_info.cpu_type,
host_basic_info.cpu_subtype, mh->cpusubtype) == -1){
error("bad CPU subtype in %s: %s", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADARCH, name);
return(FALSE);
}
if(mh->sizeofcmds + sizeof(struct mach_header) > image_size){
error("truncated or malformed %s: %s (load commands extend "
"past the end of the image)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
load_commands = (struct load_command *)((char *)mh +
sizeof(struct mach_header));
lc = load_commands;
for(i = 0; i < mh->ncmds; i++){
if(lc->cmdsize % sizeof(long) != 0){
error("truncated or malformed %s: %s (load command %lu "
"size not a multiple of sizeof(long))",image_type, name, i);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(lc->cmdsize == 0){
error("truncated or malformed %s: %s (load command %lu "
"size is equal to zero)", image_type, name, i);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if((char *)lc + lc->cmdsize >
(char *)load_commands + mh->sizeofcmds){
error("truncated or malformed %s: %s (load command %lu "
"extends past end of all load commands)", image_type,
name, i);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
if(sg->fileoff > image_size){
error("truncated or malformed %s: %s (load command "
"%lu fileoff extends past end of the library)",
image_type, name, i);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(sg->fileoff + sg->filesize > image_size){
error("truncated or malformed %s: %s (load command "
"%lu fileoff plus filesize extends past end of the "
"library)", image_type, name, i);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(sg->cmdsize != sizeof(struct segment_command) +
sg->nsects * sizeof(struct section)){
error("malformed %s: %s (cmdsize field of load "
"command %lu is inconsistant for a segment command "
"with the number of sections it has)", image_type,
name, i);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(strcmp(sg->segname, SEG_LINKEDIT) == 0){
if(*linkedit_segment == NULL)
*linkedit_segment = sg;
}
if(sg->fileoff == 0)
*mach_header_segment = sg;
if(sg->vmaddr < *low_addr)
*low_addr = sg->vmaddr;
if(sg->vmaddr + sg->vmsize > *high_addr)
*high_addr = sg->vmaddr + sg->vmsize;
break;
case LC_DYSYMTAB:
if(*dyst == NULL)
*dyst = (struct dysymtab_command *)lc;
break;
case LC_SYMTAB:
if(*st == NULL)
*st = (struct symtab_command *)lc;
break;
case LC_ROUTINES:
if(*rc == NULL)
*rc = (struct routines_command *)lc;
break;
case LC_ID_DYLIB:
if(dlid != NULL && *dlid == NULL){
*dlid = (struct dylib_command *)lc;
if((*dlid)->cmdsize < sizeof(struct dylib_command)){
error("truncated or malformed %s: %s (cmdsize of "
"load command %lu incorrect for LC_ID_DYLIB)",
image_type, name, i);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
break;
case LC_LOAD_DYLIB:
dl_load = (struct dylib_command *)lc;
if(dl_load->cmdsize < sizeof(struct dylib_command)){
error("truncated or malformed %s: %s (cmdsize of load "
"command %lu incorrect for LC_LOAD_DYLIB)", image_type,
name, i);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(dl_load->dylib.name.offset >= dl_load->cmdsize){
error("truncated or malformed %s: %s (name.offset of "
"load command %lu extends past the end of the load "
"command)", image_type, name, i);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
load_dylib_name = (char *)dl_load + dl_load->dylib.name.offset;
for(j = 0;
j < dl_load->cmdsize - dl_load->dylib.name.offset;
j++){
if(load_dylib_name[j] == '\0')
break;
}
if(j >= dl_load->cmdsize - dl_load->dylib.name.offset){
error("truncated or malformed %s: %s (library name of "
"load command %lu not null terminated)", image_type,
name, i);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
break;
default:
if((lc->cmd & LC_REQ_DYLD) == LC_REQ_DYLD){
error("can't use %s: %s (unknown load command %ld (0x%x) "
"required for execution)", image_type, name, i,
(unsigned int)(lc->cmd));
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
}
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(*mach_header_segment == NULL){
error("malformed %s: %s (no segment command maps the mach_header)",
image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(*st == NULL){
error("malformed %s: %s (no symbol table command)", image_type,
name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(*dyst == NULL){
error("malformed %s: %s (no dynamic symbol table command)",
image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(*linkedit_segment == NULL){
error("malformed %s: %s (no " SEG_LINKEDIT "segment)",
image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(check_linkedit_info(name, image_type, *linkedit_segment, *st,
*dyst, rc == NULL ? NULL : *rc) == FALSE)
return(FALSE);
return(TRUE);
}
static
enum bool
check_linkedit_info(
char *name,
char *image_type,
struct segment_command *linkedit_segment,
struct symtab_command *st,
struct dysymtab_command *dyst,
struct routines_command *rc)
{
if(st->nsyms != 0){
if(st->symoff < linkedit_segment->fileoff ||
st->symoff > linkedit_segment->fileoff +
linkedit_segment->filesize){
error("malformed %s: %s (offset to symbol table not in "
SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(st->symoff + st->nsyms * sizeof(struct nlist) <
linkedit_segment->fileoff ||
st->symoff + st->nsyms * sizeof(struct nlist) >
linkedit_segment->fileoff + linkedit_segment->filesize){
error("malformed %s: %s (symbol table not contained in "
SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
if(st->strsize != 0){
if(st->stroff < linkedit_segment->fileoff ||
st->stroff > linkedit_segment->fileoff +
linkedit_segment->filesize){
error("malformed %s: %s (offset to string table not in "
SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(st->stroff + st->strsize < linkedit_segment->fileoff ||
st->stroff + st->strsize >
linkedit_segment->fileoff + linkedit_segment->filesize){
error("malformed %s: %s (string table not contained in "
SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
if(dyst->nlocalsym != 0){
if(dyst->ilocalsym > st->nsyms){
error("malformed %s: %s (ilocalsym in LC_DYSYMTAB load command "
"extends past the end of the symbol table", image_type,
name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(dyst->ilocalsym + dyst->nlocalsym > st->nsyms){
error("malformed %s: %s (ilocalsym plus nlocalsym in "
"LC_DYSYMTAB load command extends past the end of the "
"symbol table", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
if(dyst->nextdefsym != 0){
if(dyst->iextdefsym > st->nsyms){
error("malformed %s: %s (iextdefsym in LC_DYSYMTAB load "
"command extends past the end of the symbol table",
image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(dyst->iextdefsym + dyst->nextdefsym > st->nsyms){
error("malformed %s: %s (iextdefsym plus nextdefsym in "
"LC_DYSYMTAB load command extends past the end of the "
"symbol table", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
if(dyst->nundefsym != 0){
if(dyst->iundefsym > st->nsyms){
error("malformed %s: %s (iundefsym in LC_DYSYMTAB load command "
"extends past the end of the symbol table", image_type,
name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(dyst->iundefsym + dyst->nundefsym > st->nsyms){
error("malformed %s: %s (iundefsym plus nundefsym in "
"LC_DYSYMTAB load command extends past the end of the "
"symbol table", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
if(dyst->ntoc != 0){
if(dyst->tocoff < linkedit_segment->fileoff ||
dyst->tocoff > linkedit_segment->fileoff +
linkedit_segment->filesize){
error("malformed %s: %s (offset to table of contents not in "
SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(dyst->tocoff +
dyst->ntoc * sizeof(struct dylib_table_of_contents) <
linkedit_segment->fileoff ||
dyst->tocoff +
dyst->ntoc * sizeof(struct dylib_table_of_contents) >
linkedit_segment->fileoff + linkedit_segment->filesize){
error("malformed %s: %s (table of contents not contained in "
SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
if(dyst->nmodtab != 0){
if(dyst->modtaboff < linkedit_segment->fileoff ||
dyst->modtaboff > linkedit_segment->fileoff +
linkedit_segment->filesize){
error("malformed %s: %s (offset to module table not in "
SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(dyst->modtaboff + dyst->nmodtab * sizeof(struct dylib_module) <
linkedit_segment->fileoff ||
dyst->modtaboff + dyst->nmodtab * sizeof(struct dylib_module) >
linkedit_segment->fileoff + linkedit_segment->filesize){
error("malformed %s: %s (module table not contained in "
SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(rc != NULL){
if(rc->init_module > dyst->nmodtab){
error("malformed %s: %s (module table index for "
"initialization routine not contained in module table)",
image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
}
if(dyst->nextrefsyms != 0){
if(dyst->extrefsymoff < linkedit_segment->fileoff ||
dyst->extrefsymoff > linkedit_segment->fileoff +
linkedit_segment->filesize){
error("malformed %s: %s (offset to referenced symbol table not "
"in " SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(dyst->extrefsymoff +
dyst->nextrefsyms * sizeof(struct dylib_reference) <
linkedit_segment->fileoff ||
dyst->extrefsymoff +
dyst->nextrefsyms * sizeof(struct dylib_reference) >
linkedit_segment->fileoff + linkedit_segment->filesize){
error("malformed %s: %s (referenced table not contained in "
SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
if(dyst->nindirectsyms != 0){
if(dyst->indirectsymoff < linkedit_segment->fileoff ||
dyst->indirectsymoff > linkedit_segment->fileoff +
linkedit_segment->filesize){
error("malformed %s: %s (offset to indirect symbol table not "
"in " SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(dyst->indirectsymoff +
dyst->nindirectsyms * sizeof(unsigned long) <
linkedit_segment->fileoff ||
dyst->indirectsymoff +
dyst->nindirectsyms * sizeof(unsigned long) >
linkedit_segment->fileoff + linkedit_segment->filesize){
error("malformed %s: %s (indirect symbol table not contained "
"in " SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
if(dyst->nextrel != 0){
if(dyst->extreloff < linkedit_segment->fileoff ||
dyst->extreloff > linkedit_segment->fileoff +
linkedit_segment->filesize){
error("malformed %s: %s (offset to external relocation entries "
"not in " SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(dyst->extreloff +
dyst->nextrel * sizeof(struct relocation_info) <
linkedit_segment->fileoff ||
dyst->extreloff +
dyst->nextrel * sizeof(struct relocation_info) >
linkedit_segment->fileoff + linkedit_segment->filesize){
error("malformed %s: %s (external relocation entries not "
"contained in " SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
if(dyst->nlocrel != 0){
if(dyst->locreloff < linkedit_segment->fileoff ||
dyst->locreloff > linkedit_segment->fileoff +
linkedit_segment->filesize){
error("malformed %s: %s (offset to local relocation entries "
"not in " SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
if(dyst->locreloff +
dyst->nlocrel * sizeof(struct relocation_info) <
linkedit_segment->fileoff ||
dyst->locreloff +
dyst->nlocrel * sizeof(struct relocation_info) >
linkedit_segment->fileoff + linkedit_segment->filesize){
error("malformed %s: %s (local relocation entries not "
"contained in " SEG_LINKEDIT " segment)", image_type, name);
link_edit_error(DYLD_FILE_FORMAT, EBADMACHO, name);
return(FALSE);
}
}
return(TRUE);
}
char *
save_string(
char *string)
{
unsigned long len;
char *p;
len = strlen(string) + 1;
if(len <= STRING_BLOCK_SIZE - string_block.used){
p = string_block.strings + string_block.used;
strcpy(p, string);
string_block.used += len;
}
else{
p = allocate(len);
strcpy(p, string);
}
return(p);
}
#ifdef __OPENSTEP__
extern char *realpath(const char *pathname, char resolvedname[MAXPATHLEN]);
char *
getcwd(
char *buf,
size_t size)
{
if(size == 0)
return(getwd(allocate(MAXPATHLEN + 1)));
if(size >= MAXPATHLEN)
return(getwd(buf));
return(NULL);
}
#endif
void
create_executables_path(
char *exec_path)
{
char *p, *cwd;
unsigned long n, max, cwd_len, executables_pathlen;
n = strlen(exec_path);
max = STRING_BLOCK_SIZE - string_block.used;
executables_path = string_block.strings + string_block.used;
if(exec_path[0] == '/'){
executables_pathlen = n + 1;
if(executables_pathlen <= max)
string_block.used += executables_pathlen;
else
executables_path = allocate(executables_pathlen);
p = executables_path;
}
else{
if(getcwd(executables_path, max) != NULL){
cwd_len = strlen(executables_path);
executables_pathlen = cwd_len + 1 + n + 1;
if(executables_pathlen <= max){
string_block.used += executables_pathlen;
p = executables_path + cwd_len;
}
else{
p = allocate(executables_pathlen);
strncpy(p, executables_path, cwd_len);
p = p + cwd_len;
}
}
else if((cwd = getcwd(NULL, 0)) != NULL){
cwd_len = strlen(cwd);
executables_pathlen = cwd_len + 1 + n + 1;
executables_path = allocate(executables_pathlen);
p = executables_path;
strncpy(p, cwd, cwd_len);
p = p + cwd_len;
free(cwd);
}
else{
executables_path = NULL;
if(dyld_executable_path_debug == TRUE)
printf("executables_path = NULL (getcwd() failed "
"errno = %d)\n", errno);
return;
}
*p = '/';
p++;
}
strcpy(p, exec_path);
}
static
struct object_image *
new_object_image(
void)
{
struct object_images *p;
unsigned long i;
enum link_state link_state;
for(p = &object_images ; ; p = p->next_images){
for(i = 0; i < p->nimages; i++){
link_state = GET_LINK_STATE(p->images[i].module);
if(link_state == UNUSED)
return(p->images + i);
}
if(p->nimages != NOBJECT_IMAGES){
return(p->images + p->nimages++);
}
if(p->next_images == NULL)
break;
}
p->next_images = allocate(sizeof(struct object_images));
memset(p->next_images, '\0', sizeof(struct object_images));
return(p->next_images->images + p->next_images->nimages++);
}
struct object_image *
find_object_image(
struct image *image)
{
struct object_images *p;
unsigned long i;
enum link_state link_state;
for(p = &object_images ; ; p = p->next_images){
for(i = 0; i < p->nimages; i++){
link_state = GET_LINK_STATE(p->images[i].module);
if(link_state == UNUSED)
continue;
if(image == &(p->images[i].image))
return(p->images + i);
}
if(p->next_images == NULL)
break;
}
return(NULL);
}
static
struct library_image *
new_library_image(
unsigned long nmodules)
{
struct library_images *p;
struct library_image *library_image;
for(p = &library_images ; ; p = p->next_images){
if(p->nimages != NLIBRARY_IMAGES){
library_image = p->images + p->nimages++;
library_image->nmodules = nmodules;
library_image->modules = allocate_module_states(nmodules);
memset(library_image->modules, '\0',
sizeof(module_state) * nmodules);
return(library_image);
}
if(p->next_images == NULL)
break;
}
p->next_images = allocate(sizeof(struct library_images));
memset(p->next_images, '\0', sizeof(struct library_images));
library_image = p->next_images->images + p->next_images->nimages++;
library_image->nmodules = nmodules;
library_image->modules = allocate_module_states(nmodules);
memset(library_image->modules, '\0', sizeof(module_state) * nmodules);
return(library_image);
}
static
module_state *
allocate_module_states(
unsigned long nmodules)
{
module_state *p;
if(nmodules <= MODULE_STATE_BLOCK_SIZE - module_state_block.used &&
return_on_error == FALSE){
p = module_state_block.module_states + module_state_block.used;
module_state_block.used += nmodules;
}
else{
p = allocate(sizeof(module_state) * nmodules);
}
return(p);
}
static
void
deallocate_module_states(
module_state *modules,
unsigned long nmodules)
{
if(modules < module_state_block.module_states &&
modules + nmodules >= module_state_block.module_states +
MODULE_STATE_BLOCK_SIZE){
free(modules);
}
}
static
struct image **
allocate_image_pointers(
unsigned long n)
{
struct image **p;
if(n <= IMAGE_POINTER_BLOCK_SIZE - image_pointers_block.used &&
return_on_error == FALSE){
p = image_pointers_block.image_pointers + image_pointers_block.used;
image_pointers_block.used += n;
}
else{
p = allocate(sizeof(struct image *) * n);
}
return(p);
}
static
void
reallocate_image_pointers(
struct image **image_pointers,
unsigned long old_size,
unsigned long new_size)
{
if(image_pointers < image_pointers_block.image_pointers &&
image_pointers + old_size >= image_pointers_block.image_pointers +
IMAGE_POINTER_BLOCK_SIZE){
return;
}
else{
if(old_size >= new_size &&
image_pointers + old_size ==
image_pointers_block.image_pointers + image_pointers_block.used){
memset(image_pointers + new_size, '\0',
sizeof(struct image *) * (old_size - new_size));
image_pointers_block.used -= (old_size - new_size);
}
}
}
static
void
deallocate_image_pointers(
struct image **image_pointers,
unsigned long n)
{
if(image_pointers == NULL || n == 0)
return;
if(image_pointers < image_pointers_block.image_pointers &&
image_pointers + n >= image_pointers_block.image_pointers +
IMAGE_POINTER_BLOCK_SIZE){
free(image_pointers);
}
else{
if(image_pointers + n ==
image_pointers_block.image_pointers + image_pointers_block.used){
memset(image_pointers, '\0', sizeof(struct image *) * n);
image_pointers_block.used -= n;
}
}
}
static
enum bool
validate_library(
char *dylib_name,
struct dylib_command *dl,
struct library_image *li)
{
if(dl != NULL &&
dl->dylib.compatibility_version >
li->dlid->dylib.compatibility_version){
error("version mismatch for library: %s (compatibility "
"version of user: %lu.%lu.%lu greater than "
"library's version: %lu.%lu.%lu)", dylib_name,
dl->dylib.compatibility_version >> 16,
(dl->dylib.compatibility_version >> 8) & 0xff,
dl->dylib.compatibility_version & 0xff,
li->dlid->dylib.compatibility_version >> 16,
(li->dlid->dylib.compatibility_version >> 8) & 0xff,
li->dlid->dylib.compatibility_version & 0xff);
link_edit_error(DYLD_FILE_FORMAT, ESHLIBVERS, dylib_name);
return(FALSE);
}
if(dl == NULL ||
dl->dylib.timestamp != li->dlid->dylib.timestamp){
if(dyld_prebind_debug != 0 &&
prebinding == TRUE &&
launched == FALSE)
print("dyld: %s: prebinding disabled because time stamp of "
"library: %s did not match\n", executables_name,
dylib_name);
if(launched == FALSE)
prebinding = FALSE;
}
return(TRUE);
}
enum bool
is_library_loaded_by_name(
char *dylib_name,
struct dylib_command *dl,
struct image **image_pointer)
{
unsigned long i;
struct library_images *p;
if(launched == FALSE && *dylib_name != '/')
return(FALSE);
for(p = &library_images; p != NULL; p = p->next_images){
for(i = 0; i < p->nimages; i++){
if(strcmp(dylib_name, p->images[i].image.name) == 0){
if(validate_library(dylib_name, dl, &(p->images[i])) ==
TRUE){
if(image_pointer != NULL)
*image_pointer = &(p->images[i].image);
return(TRUE);
}
}
}
}
return(FALSE);
}
enum bool
is_library_loaded_by_stat(
char *dylib_name,
struct dylib_command *dl,
struct stat *stat_buf,
struct image **image_pointer)
{
unsigned long i;
struct library_images *p;
for(p = &library_images; p != NULL; p = p->next_images){
for(i = 0; i < p->nimages; i++){
if(stat_buf->st_dev == p->images[i].dev &&
stat_buf->st_ino == p->images[i].ino){
if(validate_library(dylib_name, dl, &(p->images[i])) ==
TRUE){
if(image_pointer != NULL)
*image_pointer = &(p->images[i].image);
return(TRUE);
}
}
}
}
return(FALSE);
}
enum bool
set_images_to_prebound(
void)
{
unsigned long i, j;
struct load_command *lc;
struct prebound_dylib_command *pbdylib;
struct library_images *q;
#ifdef __MACH30__
enum bool lazy_inits;
lazy_inits = FALSE;
#endif __MACH30__
lc = (struct load_command *)((char *)object_images.images[0].image.mh +
sizeof(struct mach_header));
for(i = 0; i < object_images.images[0].image.mh->ncmds; i++){
switch(lc->cmd){
case LC_PREBOUND_DYLIB:
pbdylib = (struct prebound_dylib_command *)lc;
if(set_prebound_state(pbdylib) == FALSE){
reset_module_states();
return(FALSE);
}
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
#ifdef __MACH30__
if(q->images[i].image.mh->flags & MH_LAZY_INIT)
lazy_inits = TRUE;
#endif __MACH30__
if(q->images[i].image.prebound != TRUE){
if(dyld_prebind_debug != 0)
print("dyld: %s: prebinding disabled because no "
"LC_PREBOUND_DYLIB for library: %s\n",
executables_name,
(char *)q->images[i].dlid +
q->images[i].dlid->dylib.name.offset);
prebinding = FALSE;
reset_module_states();
return(FALSE);
}
}
q = q->next_images;
}while(q != NULL);
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
for(j = 0; j < q->images[i].nmodules; j++)
if(q->images[i].modules[j] != FULLY_LINKED)
SET_LINK_STATE(q->images[i].modules[j],
PREBOUND_UNLINKED);
}
q = q->next_images;
}while(q != NULL);
SET_LINK_STATE(object_images.images[0].module, FULLY_LINKED);
setup_prebound_coalesed_symbols();
#ifdef __MACH30__
if(lazy_inits == TRUE)
setup_for_lazy_init_routines();
#endif __MACH30__
call_registered_funcs_for_add_images();
return(TRUE);
}
static
enum bool
set_prebound_state(
struct prebound_dylib_command *pbdylib)
{
unsigned long i, j;
char *name, *linked_modules, *install_name;
struct library_images *q;
enum bool some_modules_linked;
name = (char *)pbdylib + pbdylib->name.offset;
linked_modules = (char *)pbdylib + pbdylib->linked_modules.offset;
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
install_name = (char *)q->images[i].dlid +
q->images[i].dlid->dylib.name.offset;
if(strcmp(install_name, name) == 0){
some_modules_linked = FALSE;
if(q->images[i].image.prebound == TRUE ||
q->images[i].image.dyst->nmodtab != pbdylib->nmodules){
if(dyld_prebind_debug != 0)
print("dyld: %s: prebinding disabled because "
"nmodules in LC_PREBOUND_DYLIB for library: "
"%s does not match\n",executables_name,name);
prebinding = FALSE;
return(FALSE);
}
for(j = 0; j < q->images[i].nmodules; j++){
if((linked_modules[j/8] >> (j%8)) & 1){
SET_LINK_STATE(q->images[i].modules[j],
FULLY_LINKED);
some_modules_linked = TRUE;
}
}
q->images[i].image.prebound = TRUE;
if(some_modules_linked == FALSE){
undo_prebound_lazy_pointers(
&(q->images[i].image),
#if defined(m68k) || defined(__i386__)
GENERIC_RELOC_PB_LA_PTR,
#endif
#ifdef hppa
HPPA_RELOC_PB_LA_PTR,
#endif
#ifdef sparc
SPARC_RELOC_PB_LA_PTR,
#endif
#ifdef __ppc__
PPC_RELOC_PB_LA_PTR,
#endif
TRUE,
0);
q->images[i].image.undone_prebound_lazy_pointers = TRUE;
}
return(TRUE);
}
}
q = q->next_images;
}while(q != NULL);
if(dyld_prebind_debug != 0)
print("dyld: %s: prebinding disabled because LC_PREBOUND_DYLIB "
"found for library: %s but it was not loaded\n",
executables_name, name);
prebinding = FALSE;
return(FALSE);
}
void
undo_prebound_images(
void)
{
unsigned long i;
struct object_images *p;
struct library_images *q;
struct segment_command *linkedit_segment;
struct symtab_command *st;
struct dysymtab_command *dyst;
struct relocation_info *relocs;
struct nlist *symbols;
char *strings;
enum link_state link_state;
p = &object_images;
do{
for(i = 0; i < p->nimages; i++){
link_state = GET_LINK_STATE(p->images[i].module);
if(link_state == UNUSED)
continue;
if((p->images[i].image.mh->flags & MH_PREBOUND) != MH_PREBOUND)
continue;
if(p->images[i].image.change_protect_on_reloc){
make_image_writable(&(p->images[i].image), "object");
}
undo_prebound_lazy_pointers(
&(p->images[i].image),
#if defined(m68k) || defined(__i386__)
GENERIC_RELOC_PB_LA_PTR,
#endif
#ifdef hppa
HPPA_RELOC_PB_LA_PTR,
#endif
#ifdef sparc
SPARC_RELOC_PB_LA_PTR,
#endif
#ifdef __ppc__
PPC_RELOC_PB_LA_PTR,
#endif
TRUE,
0);
p->images[i].image.undone_prebound_lazy_pointers = TRUE;
linkedit_segment = p->images[i].image.linkedit_segment;
st = p->images[i].image.st;
dyst = p->images[i].image.dyst;
if(linkedit_segment != NULL && st != NULL && dyst != NULL){
relocs = (struct relocation_info *)
(p->images[i].image.vmaddr_slide +
linkedit_segment->vmaddr +
dyst->extreloff -
linkedit_segment->fileoff);
symbols = (struct nlist *)
(p->images[i].image.vmaddr_slide +
linkedit_segment->vmaddr +
st->symoff -
linkedit_segment->fileoff);
strings = (char *)
(p->images[i].image.vmaddr_slide +
linkedit_segment->vmaddr +
st->stroff -
linkedit_segment->fileoff);
undo_external_relocation(
TRUE,
&(p->images[i].image),
relocs,
dyst->nextrel,
symbols,
strings,
NULL,
p->images[i].image.name);
}
if(p->images[i].image.change_protect_on_reloc){
restore_image_vm_protections(&(p->images[i].image),
"object");
}
}
p = p->next_images;
}while(p != NULL);
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
if((q->images[i].image.mh->flags & MH_PREBOUND) != MH_PREBOUND)
continue;
undo_prebinding_for_library(q->images + i);
}
q = q->next_images;
}while(q != NULL);
}
static
void
undo_prebinding_for_library(
struct library_image *library_image)
{
unsigned long j;
if(library_image->image.subtrees_twolevel_prebound == FALSE){
if(library_image->image.vmaddr_slide == 0){
undo_prebound_lazy_pointers(
&(library_image->image),
#if defined(m68k) || defined(__i386__)
GENERIC_RELOC_PB_LA_PTR,
#endif
#ifdef hppa
HPPA_RELOC_PB_LA_PTR,
#endif
#ifdef sparc
SPARC_RELOC_PB_LA_PTR,
#endif
#ifdef __ppc__
PPC_RELOC_PB_LA_PTR,
#endif
TRUE,
0);
library_image->image.undone_prebound_lazy_pointers = TRUE;
}
for(j = 0; j < library_image->nmodules; j++)
SET_LINK_STATE(library_image->modules[j], PREBOUND_UNLINKED);
}
else{
if(dyld_prebind_debug > 2){
print("dyld: In undo_prebinding_for_library() name = %s "
"setup = %s twolevel_prebound = %s\n",
library_image->image.name,
library_image->image.subtrees_twolevel_prebound_setup ==
TRUE ? "TRUE" : "FALSE",
library_image->image.subtrees_twolevel_prebound ==
TRUE ? "TRUE" : "FALSE");
}
for(j = 0; j < library_image->nmodules; j++){
SET_LINK_STATE(library_image->modules[j], FULLY_LINKED);
SET_FULLYBOUND_STATE(library_image->modules[j]);
}
}
}
void
try_to_use_prebound_libraries(
void)
{
if(library_images.nimages > 1 ||
(force_flat_namespace == FALSE &&
library_images.nimages == 1 &&
(library_images.images[0].image.mh->flags & MH_TWOLEVEL) ==
MH_TWOLEVEL) ){
prebinding = FALSE;
return;
}
if(check_executable_for_overrides() == FALSE)
return;
if(check_libraries_for_overrides() == FALSE)
return;
setup_initial_undefined_list(TRUE);
if(resolve_undefineds(TRUE, TRUE) == FALSE){
failed_use_prebound_libraries();
return;
}
if(undefined_list.next != &undefined_list){
if(dyld_prebind_debug != 0 && prebinding == TRUE)
print("dyld: %s: trying to use prebound libraries failed due "
"to undefined symbols\n", executables_name);
prebinding = FALSE;
failed_use_prebound_libraries();
return;
}
relocate_modules_being_linked(TRUE);
SET_LINK_STATE(object_images.images[0].module, FULLY_LINKED);
setup_prebound_coalesed_symbols();
call_registered_funcs_for_add_images();
}
static
void
failed_use_prebound_libraries(
void)
{
clear_undefined_list(FALSE);
clear_being_linked_list(FALSE);
reset_module_states();
}
static
void
reset_module_states(
void)
{
unsigned long i, j;
struct library_images *q;
SET_LINK_STATE(object_images.images[0].module, BEING_LINKED);
CLEAR_FULLYBOUND_STATE(object_images.images[0].module);
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
q->images[i].image.init_bound = FALSE;
for(j = 0; j < q->images[i].nmodules; j++){
SET_LINK_STATE(q->images[i].modules[j], UNLINKED);
CLEAR_FULLYBOUND_STATE(q->images[i].modules[j]);
}
}
q = q->next_images;
}while(q != NULL);
}
#ifdef __ppc__
#include "fp_save_restore.h"
#endif
void
call_image_init_routines(
enum bool make_delayed_calls)
{
unsigned long i, addr;
struct library_images *q;
module_state *module;
void (*init_routine)(void);
enum link_state link_state;
#ifdef __ppc__
double fp_save_area[N_FP_REGS];
unsigned long vec_save_area[N_VEC_REGS * 4] __attribute__ ((aligned(16)));
enum bool saved_regs = FALSE;
#if !defined(__GONZO_BUNSEN_BEAKER__) && !defined(__HERA__)
int facilities_used = -1;
#endif
#endif
static enum bool delay_init_routines = TRUE;
if(delay_init_routines == TRUE && make_delayed_calls == FALSE)
return;
if(make_delayed_calls == TRUE)
delay_init_routines = FALSE;
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
if(q->images[i].image.rc != NULL &&
q->images[i].image.init_called == FALSE){
module = q->images[i].modules +
q->images[i].image.rc->init_module;
link_state = GET_LINK_STATE(*module);
if(link_state != UNLINKED &&
link_state != PREBOUND_UNLINKED){
#ifdef __ppc__
#if !defined(__GONZO_BUNSEN_BEAKER__) && !defined(__HERA__)
if(facilities_used == -1)
facilities_used = processor_facilities_used();
#endif
if(saved_regs == FALSE){
#if !defined(__GONZO_BUNSEN_BEAKER__) && !defined(__HERA__)
if(facilities_used & floatUsed)
#endif
ppc_fp_save(fp_save_area);
#if defined(__GONZO_BUNSEN_BEAKER__) || defined(__HERA__)
if(processor_has_vec == TRUE)
#else
if(_cpu_has_altivec == TRUE &&
(facilities_used & vectorUsed))
#endif
ppc_vec_save(vec_save_area);
saved_regs = TRUE;
}
#endif
q->images[i].image.init_called = TRUE;
call_dependent_init_routines(
q->images + i,
&(q->images[i].image),
module,
make_delayed_calls == TRUE && prebinding == TRUE);
if(q->images[i].image.lazy_init == FALSE){
addr = q->images[i].image.rc->init_address +
q->images[i].image.vmaddr_slide;
init_routine = (void(*)(void))addr;
if(init_routine_being_called == FALSE)
init_routine_being_called = TRUE;
else if(dyld_abort_multiple_inits == TRUE)
abort();
release_lock();
init_routine();
set_lock();
init_routine_being_called = FALSE;
}
}
}
}
q = q->next_images;
}while(q != NULL);
#ifdef __ppc__
if(saved_regs == TRUE){
#if !defined(__GONZO_BUNSEN_BEAKER__) && !defined(__HERA__)
if(facilities_used & floatUsed)
#endif
ppc_fp_restore(fp_save_area);
#if defined(__GONZO_BUNSEN_BEAKER__) || defined(__HERA__)
if(processor_has_vec == TRUE)
#else
if(_cpu_has_altivec == TRUE && (facilities_used & vectorUsed))
#endif
ppc_vec_restore(vec_save_area);
}
#endif
}
static
void
call_dependent_init_routines(
struct library_image *library_image,
struct image *image,
module_state *module,
enum bool use_header_dependencies)
{
unsigned long i;
struct segment_command *linkedit_segment;
struct symtab_command *st;
struct dysymtab_command *dyst;
struct nlist *symbols;
char *strings;
struct dylib_module *dylib_modules, *dylib_module;
unsigned long module_index;
struct dylib_reference *dylib_references;
char *symbol_name, *module_name;
struct nlist *defined_symbol;
module_state *defined_module;
struct image *defined_image;
struct library_image *defined_library_image;
unsigned long addr;
void (*init_routine)(void);
unsigned long j;
struct load_command *lc;
struct dylib_command *dl;
struct library_images *q;
char *dependent_name;
struct library_image *dependent_library_image;
struct image *dependent_image;
module_state *dependent_module;
enum link_state link_state;
if(use_header_dependencies == FALSE || module != NULL)
SET_IMAGE_INIT_DEPEND_STATE(*module);
linkedit_segment = image->linkedit_segment;
st = image->st;
dyst = image->dyst;
symbols = (struct nlist *)
(image->vmaddr_slide +
linkedit_segment->vmaddr +
st->symoff -
linkedit_segment->fileoff);
strings = (char *)
(image->vmaddr_slide +
linkedit_segment->vmaddr +
st->stroff -
linkedit_segment->fileoff);
if(library_image == NULL){
for(i = dyst->iundefsym;
i < dyst->iundefsym + dyst->nundefsym;
i++){
symbol_name = strings + symbols[i].n_un.n_strx;
lookup_symbol(symbol_name, get_primary_image(image, symbols+i),
get_hint(image, symbols+i),
&defined_symbol, &defined_module,
&defined_image, &defined_library_image,
NO_INDR_LOOP);
if(defined_symbol == NULL){
continue;
}
if(GET_IMAGE_INIT_DEPEND_STATE(*module) != 0){
call_dependent_init_routines(
defined_library_image,
defined_image,
defined_module,
use_header_dependencies);
}
}
}
else{
dylib_modules = (struct dylib_module *)
(image->vmaddr_slide +
linkedit_segment->vmaddr +
dyst->modtaboff -
linkedit_segment->fileoff);
if(module != NULL){
module_index = module - library_image->modules;
dylib_module = dylib_modules + module_index;
module_name = strings + dylib_module->module_name;
}
else{
dylib_module = NULL;
module_name = "NULL";
}
if(use_header_dependencies == TRUE){
lc = (struct load_command *)((char *)image->mh +
sizeof(struct mach_header));
for(i = 0; i < image->mh->ncmds; i++){
if(lc->cmd == LC_LOAD_DYLIB){
dl = (struct dylib_command *)lc;
dependent_name = (char *)dl + dl->dylib.name.offset;
dependent_image = NULL;
dependent_library_image = NULL;
q = &library_images;
do{
for(j = 0;
dependent_image == NULL && j < q->nimages;
j++){
if(strcmp((char *)q->images[j].dlid +
q->images[j].dlid->dylib.name.offset,
dependent_name) == 0){
dependent_image = &(q->images[j].image);
dependent_library_image = q->images + j;
}
}
q = q->next_images;
}while(dependent_image == NULL && q != NULL);
if(dependent_image != NULL &&
dependent_library_image->image.init_called == FALSE){
call_dependent_init_routines(
dependent_library_image,
dependent_image,
NULL,
use_header_dependencies);
}
if(dependent_image != NULL &&
dependent_image->rc != NULL &&
dependent_library_image->image.init_called == FALSE){
dependent_module =
dependent_library_image->modules +
dependent_image->rc->init_module;
link_state = GET_LINK_STATE(*dependent_module);
if(link_state != UNLINKED &&
link_state != PREBOUND_UNLINKED){
dependent_library_image->image.init_called =
TRUE;
if(dependent_library_image->image.lazy_init ==
FALSE){
addr = dependent_library_image->
image.rc->init_address +
dependent_library_image->
image.vmaddr_slide;
init_routine = (void(*)(void))addr;
if(init_routine_being_called == FALSE)
init_routine_being_called = TRUE;
else if(dyld_abort_multiple_inits == TRUE)
abort();
release_lock();
init_routine();
set_lock();
init_routine_being_called = FALSE;
}
}
}
else if(dependent_library_image != NULL){
dependent_library_image->image.init_called = TRUE;
}
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
return;
}
dylib_references = (struct dylib_reference *)
(image->vmaddr_slide +
linkedit_segment->vmaddr +
dyst->extrefsymoff -
linkedit_segment->fileoff);
for(i = dylib_module->irefsym;
i < dylib_module->irefsym + dylib_module->nrefsym;
i++){
symbol_name = strings +
symbols[dylib_references[i].isym].n_un.n_strx;
if(dylib_references[i].flags !=
REFERENCE_FLAG_UNDEFINED_NON_LAZY &&
dylib_references[i].flags !=
REFERENCE_FLAG_UNDEFINED_LAZY)
continue;
lookup_symbol(symbol_name, get_primary_image(image, symbols +
dylib_references[i].isym),
get_hint(image, symbols+dylib_references[i].isym),
&defined_symbol, &defined_module,
&defined_image, &defined_library_image,
NO_INDR_LOOP);
if(defined_symbol == NULL){
continue;
}
if(GET_IMAGE_INIT_DEPEND_STATE(*defined_module) == 0){
call_dependent_init_routines(
defined_library_image,
defined_image,
defined_module,
use_header_dependencies);
}
if(defined_library_image != NULL &&
defined_library_image->image.rc != NULL &&
defined_library_image->image.init_called == FALSE){
defined_library_image->image.init_called = TRUE;
if(defined_library_image->image.lazy_init == FALSE){
addr = defined_library_image->image.rc->init_address +
defined_library_image->image.vmaddr_slide;
init_routine = (void(*)(void))addr;
if(init_routine_being_called == FALSE)
init_routine_being_called = TRUE;
else if(dyld_abort_multiple_inits == TRUE)
abort();
release_lock();
init_routine();
set_lock();
init_routine_being_called = FALSE;
}
}
}
}
}
#ifdef __MACH30__
static
void
setup_for_lazy_init_routines(
void)
{
struct library_images *q;
unsigned long i, j;
struct load_command *lc, *load_commands;
struct segment_command *sg;
vm_address_t address;
kern_return_t r;
enum bool set_up_handler;
mach_port_t my_exception_port;
mach_msg_type_number_t old_exception_count;
thread_state_flavor_t my_flavor;
int result;
pthread_t pthread;
set_up_handler = FALSE;
q = &library_images;
do{
for(i = 0; i < q->nimages; i++){
if((q->images[i].image.mh->flags & MH_LAZY_INIT) != 0 &&
q->images[i].image.rc != NULL){
load_commands = (struct load_command *)
((char *)q->images[i].image.mh +
sizeof(struct mach_header));
lc = load_commands;
for(j = 0; j < q->images[i].image.mh->ncmds; j++){
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
address = sg->vmaddr +
q->images[i].image.vmaddr_slide;
if((sg->initprot & VM_PROT_WRITE) != 0 &&
address != (vm_address_t)q->images[i].image.mh &&
sg != q->images[i].image.linkedit_segment){
if((r = vm_protect(mach_task_self(), address,
(vm_size_t)sg->vmsize,
FALSE, VM_PROT_NONE)) !=
KERN_SUCCESS){
mach_error(r, "can't vm_protect segment: "
"%.16s for library: %s", sg->segname,
q->images[i].image.name);
link_edit_error(DYLD_MACH_RESOURCE, r,
q->images[i].image.name);
}
q->images[i].image.lazy_init = TRUE;
set_up_handler = TRUE;
}
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
}
}
q = q->next_images;
}while(q != NULL);
if(set_up_handler == FALSE)
return;
r = mach_port_allocate(
mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&my_exception_port);
if(r != KERN_SUCCESS){
mach_error(r, "can't allocate exception port, mach_port_allocate() "
"failed");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
r = mach_port_insert_right(
mach_task_self(),
my_exception_port,
my_exception_port,
MACH_MSG_TYPE_MAKE_SEND);
if(r != KERN_SUCCESS){
mach_error(r, "can't insert send rights on exception port, "
"mach_port_insert_right() failed");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
old_exception_count = 1;
r = task_get_exception_ports(
mach_task_self(),
EXC_MASK_BAD_ACCESS,
old_exception_masks,
&old_exception_count,
old_exception_ports,
old_behaviors,
old_flavors);
if(r != KERN_SUCCESS){
mach_error(r, "can't get old exception port, "
"task_get_exception_ports() failed");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
if(old_exception_count != 1){
error("unexpected value returned from task_get_exception_ports() "
"old_exception_count (%d) value not 1 for a single "
"exception_type (EXC_MASK_BAD_ACCESS)", old_exception_count);
link_edit_error(DYLD_OTHER_ERROR, DYLD_LAZY_INIT, NULL);
}
if(old_behaviors[0] == EXCEPTION_DEFAULT){
#ifdef __ppc__
my_flavor = PPC_THREAD_STATE;
#endif
#ifdef __i386__
my_flavor = i386_THREAD_STATE;
#endif
#ifdef m68k
my_flavor = M68K_THREAD_STATE_REGS;
#endif
#ifdef hppa
my_flavor = HPPA_FRAME_THREAD_STATE;
#endif
#ifdef sparc
my_flavor = SPARC_THREAD_STATE_REGS;
#endif
}
else{
my_flavor = old_flavors[0];
}
r = task_set_exception_ports(
mach_task_self(),
EXC_MASK_BAD_ACCESS,
my_exception_port,
EXCEPTION_STATE_IDENTITY,
my_flavor);
if(r != KERN_SUCCESS){
mach_error(r, "can't set exception port, "
"task_set_exception_ports() failed");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
result = pthread_create(
&pthread,
NULL,
(void *(*)(void *))
exception_server_loop,
(void *)
my_exception_port);
if(result != 0){
system_error(result, "can't create a pthread for exception server "
"loop");
link_edit_error(DYLD_UNIX_RESOURCE, result, NULL);
}
}
static
void
exception_server_loop(
mach_port_t my_exception_port)
{
unsigned char msg_buf[MY_MSG_SIZE], reply_buf[MY_MSG_SIZE];
mach_msg_header_t *msg, *reply;
kern_return_t r;
boolean_t eret;
msg = (mach_msg_header_t *) msg_buf;
reply = (mach_msg_header_t *) reply_buf;
for(;;){
r = mach_msg(msg,
MACH_RCV_MSG,
0,
MY_MSG_SIZE,
my_exception_port,
0,
MACH_PORT_NULL);
if(r != KERN_SUCCESS){
mach_error(r, "can't receive exception message in "
"exception_server_loop(), mach_msg() failed");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
eret = exc_server(msg, reply);
if(eret == FALSE){
error("exc_server() returned FALSE which is an internal error "
"in the way the library and user code got linked up");
link_edit_error(DYLD_OTHER_ERROR, DYLD_LAZY_INIT, NULL);
}
r = mach_msg(reply,
(MACH_SEND_MSG | MACH_MSG_OPTION_NONE),
reply->msgh_size,
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if(r != KERN_SUCCESS){
mach_error(r, "can't send reply exception message in "
"exception_server_loop(), mach_msg() failed");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
}
}
void internal_catch_exception_raise(void){}
void internal_catch_exception_raise_state(void){}
kern_return_t
internal_catch_exception_raise_state_identity(
exception_port_t exception_port,
thread_port_t thread,
task_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count,
thread_state_flavor_t *flavor,
thread_state_t in_state,
mach_msg_type_number_t in_state_count,
thread_state_t *out_state,
mach_msg_type_number_t *out_state_count)
{
mach_msg_type_number_t i;
boolean_t expected_exception, found_segment;
unsigned long exception_address;
vm_prot_t protection;
kern_return_t r;
expected_exception = FALSE;
exception_address = 0;
found_segment = FALSE;
protection = VM_PROT_NONE;
#ifdef DEBUG_LAZY_INIT_EXCEPTIONS
printf("internal_catch_exception_raise_state_identity called\n");
printf(" exception_port: 0x%x\n", exception_port);
printf(" thread: 0x%x\n", thread);
printf(" task: 0x%x\n", task);
printf(" exception: 0x%x (%s)\n", exception,
exception_name(exception));
printf( " code_count: 0x%x\n", code_count);
#endif
for(i = 0 ; i < code_count; i++){
#ifdef DEBUG_LAZY_INIT_EXCEPTIONS
printf(" code[%d]: 0x%x", i, code[i]);
#endif
if(exception == EXC_BAD_ACCESS && i == 0){
switch(code[i]){
case KERN_PROTECTION_FAILURE:
expected_exception = TRUE;
#ifdef DEBUG_LAZY_INIT_EXCEPTIONS
printf(" (code, KERN_PROTECTION_FAILURE)\n");
break;
case KERN_INVALID_ADDRESS:
printf(" (code, KERN_INVALID_ADDRESS)\n");
break;
default:
printf(" (code, unknown)\n");
#endif
break;
}
}
else if(exception == EXC_BAD_ACCESS && i == 1){
exception_address = code[i];
#ifdef DEBUG_LAZY_INIT_EXCEPTIONS
printf(" (subcode, the bad memory address)\n");
}
else{
printf("\n");
#endif
}
}
#ifdef DEBUG_LAZY_INIT_EXCEPTIONS
printf(" flavor: %d ", *flavor);
switch(*flavor){
#ifdef __ppc__
case PPC_THREAD_STATE:
printf("PPC_THREAD_STATE\n");
printf(" in_state_count: %d ", in_state_count);
if(in_state_count == PPC_THREAD_STATE_COUNT)
printf("PPC_THREAD_STATE_COUNT\n");
else
printf("(not PPC_THREAD_STATE_COUNT)\n");
break;
case PPC_FLOAT_STATE:
printf("PPC_FLOAT_STATE\n");
printf(" in_state_count: %d ", in_state_count);
if(in_state_count == PPC_FLOAT_STATE_COUNT)
printf("PPC_FLOAT_STATE_COUNT\n");
else
printf("(not PPC_FLOAT_STATE_COUNT)\n");
break;
case PPC_EXCEPTION_STATE:
printf("PPC_EXCEPTION_STATE\n");
printf(" in_state_count: %d ", in_state_count);
if(in_state_count == PPC_EXCEPTION_STATE_COUNT)
printf("PPC_EXCEPTION_STATE_COUNT\n");
else
printf("(not PPC_EXCEPTION_STATE_COUNT)\n");
break;
#endif
#ifdef __i386__
case i386_THREAD_STATE:
printf("i386_THREAD_STATE\n");
printf(" in_state_count: %d\n", in_state_count);
break;
#endif
default:
printf("(unknown state)\n");
printf(" in_state_count: %d\n", in_state_count);
break;
}
#endif
memcpy(out_state, in_state,
sizeof(mach_msg_type_number_t) * in_state_count);
*out_state_count = in_state_count;
if(expected_exception == TRUE){
if(call_lazy_init_routine_for_address(exception_address) == TRUE)
return(KERN_SUCCESS);
}
switch(old_behaviors[0]){
case EXCEPTION_DEFAULT:
#ifdef DEBUG_LAZY_INIT_EXCEPTIONS
printf("forwarding the exception with exception_raise()\n");
#endif
r = exception_raise(
old_exception_ports[0],
thread,
task,
exception,
code,
code_count);
if(r != KERN_SUCCESS){
mach_error(r, "exception_raise() failed in forwarding "
"exception");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
break;
case EXCEPTION_STATE:
#ifdef DEBUG_LAZY_INIT_EXCEPTIONS
printf("forwarding the exception with exception_raise_state()\n");
#endif
r = exception_raise_state(
old_exception_ports[0],
exception,
code,
code_count,
flavor,
in_state,
in_state_count,
out_state,
out_state_count);
if(r != KERN_SUCCESS){
mach_error(r, "exception_raise_state() failed in forwarding "
"exception");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
break;
case EXCEPTION_STATE_IDENTITY:
#ifdef DEBUG_LAZY_INIT_EXCEPTIONS
printf("forwarding the exception with "
"exception_raise_state_identity()\n");
#endif
r = exception_raise_state_identity(
old_exception_ports[0],
thread,
task,
exception,
code,
code_count,
flavor,
in_state,
in_state_count,
out_state,
out_state_count);
if(r != KERN_SUCCESS){
mach_error(r, "exception_raise_state_identity() failed in "
"forwarding exception");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
break;
default:
error("unknown old_behavior (%d) of old exception port (don't know "
"how to forward the exception)\n", old_behaviors[0]);
link_edit_error(DYLD_OTHER_ERROR, DYLD_LAZY_INIT, NULL);
}
#ifdef DEBUG_LAZY_INIT_EXCEPTIONS
printf("returning with KERN_SUCCESS from my handler for forwarded "
"exception\n");
#endif
return(KERN_SUCCESS);
}
#ifdef DEBUG_LAZY_INIT_EXCEPTIONS
static
const char *
exception_name(
exception_type_t exception)
{
switch(exception){
case EXC_BAD_ACCESS:
return("EXC_BAD_ACCESS");
case EXC_BAD_INSTRUCTION:
return("EXC_BAD_INSTRUCTION");
case EXC_ARITHMETIC:
return("EXC_ARITHMETIC");
case EXC_EMULATION:
return("EXC_EMULATION");
case EXC_SOFTWARE:
return("EXC_SOFTWARE");
case EXC_BREAKPOINT:
return("EXC_BREAKPOINT");
case EXC_SYSCALL:
return("EXC_SYSCALL");
case EXC_MACH_SYSCALL:
return("EXC_MACH_SYSCALL");
case EXC_RPC_ALERT:
return("EXC_RPC_ALERT");
default:
return("Unknown");
}
}
#endif
static
enum bool
call_lazy_init_routine_for_address(
unsigned long address)
{
struct library_images *q;
struct library_image *library_image;
unsigned long i, j;
struct load_command *lc, *load_commands;
struct segment_command *sg;
kern_return_t r;
unsigned long addr;
void (*init_routine)(void);
mach_port_t my_thread, *threads;
unsigned int thread_count;
#ifdef __ppc__
double fp_save_area[N_FP_REGS];
unsigned long vec_save_area[N_VEC_REGS * 4] __attribute__ ((aligned(16)));
enum bool saved_regs = FALSE;
#if !defined(__GONZO_BUNSEN_BEAKER__) && !defined(__HERA__)
int facilities_used = -1;
#endif
#endif
set_lock();
library_image = NULL;
for(q = &library_images; q != NULL; q = q->next_images){
for(i = 0; i < q->nimages; i++){
if((q->images[i].image.mh->flags & MH_SPLIT_SEGS) != 0){
lc = (struct load_command *)((char *)q->images[i].image.mh +
sizeof(struct mach_header));
for(j = 0; j < q->images[i].image.mh->ncmds; j++){
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
if(address >= sg->vmaddr +
q->images[i].image.vmaddr_slide &&
address < sg->vmaddr + sg->vmsize +
q->images[i].image.vmaddr_slide){
library_image = q->images + i;
goto down;
}
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
}
else{
if(address >= ((unsigned long)q->images[i].image.mh) &&
address < ((unsigned long)q->images[i].image.mh) +
q->images[i].image.vmaddr_size){
library_image = q->images + i;
goto down;
}
}
}
}
if(library_image == NULL){
release_lock();
return(FALSE);
}
down:
my_thread = mach_thread_self();
r = task_threads(mach_task_self(), &threads, &thread_count);
if(r != KERN_SUCCESS){
mach_error(r, "can't get thread list, task_threads() failed");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
for(i = 0; i < thread_count; i++){
if(threads[i] != my_thread){
r = thread_suspend(threads[i]);
if(r != KERN_SUCCESS){
mach_error(r, "can't suppend threads, thread_suspend() "
"failed");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
}
}
load_commands = (struct load_command *)
((char *)library_image->image.mh +
sizeof(struct mach_header));
lc = load_commands;
for(i = 0; i < library_image->image.mh->ncmds; i++){
switch(lc->cmd){
case LC_SEGMENT:
sg = (struct segment_command *)lc;
address = sg->vmaddr +
library_image->image.vmaddr_slide;
if((sg->initprot & VM_PROT_WRITE) != 0 &&
address != (vm_address_t)library_image->image.mh &&
sg != library_image->image.linkedit_segment){
if((r = vm_protect(mach_task_self(), address,
(vm_size_t)sg->vmsize,
FALSE, sg->initprot)) !=
KERN_SUCCESS){
mach_error(r, "can't vm_protect segment: "
"%.16s for library: %s", sg->segname,
library_image->image.name);
link_edit_error(DYLD_MACH_RESOURCE, r,
library_image->image.name);
}
}
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(library_image->image.rc != NULL &&
library_image->image.lazy_init == TRUE){
library_image->image.lazy_init = FALSE;
library_image->image.init_called = TRUE;
addr = library_image->image.rc->init_address +
library_image->image.vmaddr_slide;
init_routine = (void(*)(void))addr;
if(init_routine_being_called == FALSE)
init_routine_being_called = TRUE;
else if(dyld_abort_multiple_inits == TRUE)
abort();
release_lock();
init_routine();
set_lock();
init_routine_being_called = FALSE;
call_module_initializers_for_library(
library_image,
#ifdef __ppc__
fp_save_area,
vec_save_area,
&saved_regs,
#if !defined(__GONZO_BUNSEN_BEAKER__) && !defined(__HERA__)
&facilities_used,
#endif
#endif
TRUE ,
FALSE );
#ifdef __ppc__
if(saved_regs == TRUE){
#if !defined(__GONZO_BUNSEN_BEAKER__) && !defined(__HERA__)
if(facilities_used & floatUsed)
#endif
ppc_fp_restore(fp_save_area);
#if defined(__GONZO_BUNSEN_BEAKER__) || defined(__HERA__)
if(processor_has_vec == TRUE)
#else
if(_cpu_has_altivec == TRUE && (facilities_used & vectorUsed))
#endif
ppc_vec_restore(vec_save_area);
}
#endif
}
for(i = 0; i < thread_count; i++){
if(threads[i] != my_thread){
r = thread_resume(threads[i]);
if(r != KERN_SUCCESS){
mach_error(r, "can't resume threads, thread_resume() "
"failed");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
r = mach_port_deallocate(mach_task_self(), threads[i]);
if(r != KERN_SUCCESS){
mach_error(r, "can't deallocate port right, "
"mach_port_deallocate() failed");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
}
}
r = vm_deallocate(mach_task_self(), (vm_address_t)threads,
sizeof(threads[0]) * thread_count);
if(r != KERN_SUCCESS){
mach_error(r, "can't vm_deallocate threads list memory");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
r = mach_port_deallocate(mach_task_self(), my_thread);
if(r != KERN_SUCCESS){
mach_error(r, "can't deallocate port right, "
"mach_port_deallocate() failed");
link_edit_error(DYLD_MACH_RESOURCE, r, NULL);
}
release_lock();
return(TRUE);
}
#endif __MACH30__