/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.1 (the "License"). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * objc_hpux_register_shlib.c * Author: Laurent Ramontianu */ #warning "OBJC SHLIB SUPPORT WARNING:" #warning "Compiling objc_hpux_register_shlib.c" #warning "Shlibs containing objc code must be built using" #warning "the ld option: +I'objc_hpux_register_shlib_$(NAME)'" #warning "Be advised that if collect isn't fixed to ignore" #warning "shlibs, your app may (and will) CRASH!!!" #include #include #include #include #include #include #include #include static char *_loaded_shlibs_init[128] = { "java", "cl", "isamstub", "c", "m", "dld", "gen", "pthread", "lwp" }; static unsigned _loaded_shlibs_size = 128; static unsigned _loaded_shlibs_count = 9; static char **_loaded_shlibs = _loaded_shlibs_init; static void dump_loaded_shlibs() { int i; printf("**** Loaded shlibs ****\n"); for ( i=0; i<_loaded_shlibs_count; i++ ) { printf("\t%s\n", _loaded_shlibs[i]); } printf("--- ----\n"); } static char *my_basename(char *path) { char *res = 0; unsigned idx = strlen(path) - 1; if ( path[idx] == '/' ) idx--; for ( ; (idx > 0) && (path[idx] != '/') ; idx-- ) { if ( path[idx] == '.' ) path[idx] = '\000'; } if ( path[idx] == '/') idx++; res = strstr(&path[idx], "lib"); if ( !res ) { return &path[idx]; } if ( res == &path[idx] ) { return &path[idx+3]; } return &path[idx]; } extern void *malloc(unsigned); extern void free(void *); // Hooks if we decide to provide alternate malloc/free functions static void*(*_malloc_ptr)(unsigned) = malloc; static void(*_free_ptr)(void*) = free; static char *dep_shlibs_temp[128]; static unsigned dep_shlibs_temp_count = 0; static void dump_dependent_shlibs() { int i; printf("**** Dependent shlibs ****\n"); for ( i=0; i"); size = 0; for ( idx = 0; idx < dep_shlibs_temp_count; idx++ ) { size += sizeof(char*) + strlen(dep_shlibs_temp[idx]) + 1; } if ( ! (ptr = _malloc_ptr(size)) ) { fprintf(stderr, "dependent_shlibs: fatal - malloc() failed\n"); exit(-1); } ref_size = dep_shlibs_temp_count * sizeof(char*); size = 0; for ( idx = 0; idx < dep_shlibs_temp_count; idx++ ) { name = ptr + ref_size + size; *((char **)ptr + idx) = name; strcpy(name, dep_shlibs_temp[idx]); size += strlen(name) + 1; } dep_shlibs_temp_count = 0; return (char **)ptr; } static char **__objc_get_referenced_shlibs(char *path) { static char **dict[128]; static unsigned dict_size = 0; static char *res_nil[] = { "" }; static char file_name[48]; int fd; int child_pid; unsigned long size; void *addr; char *ptr; char *ptr2; char buf[256], *name; unsigned idx; strcpy(buf, path); name = my_basename(buf); for (idx = 0; idx < dict_size; idx++ ) { if ( ! strcmp(name, *(dict[idx])) ) return dict[idx]+1; } child_pid = vfork(); if ( child_pid < 0 ) { fprintf(stderr, "__objc_get_referenced_shlibs: fatal - vfork() failed\n"); exit(-1); } if ( child_pid > 0 ) { wait(0); sprintf(file_name, "/tmp/apple_shlib_reg.%d", child_pid); if ( (fd = open(file_name, O_RDONLY)) < 0 ) { fprintf(stderr, "__objc_get_referenced_shlibs: fatal - open() failed\n"); exit(-1); } size = lseek(fd, 0, SEEK_END); addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); init_dependent_shlibs(name); if ( ptr = strstr(addr, "list:") ) { ptr2 = strtok(ptr, " \n\t"); for ( ; ; ) { ptr2 = strtok(0, " \n\t"); if ( ! ptr2 || strcmp(ptr2, "dynamic") ) break; ptr2 = strtok(0, " \n\t"); if ( ! ptr2 ) { fprintf(stderr, "__objc_get_referenced_shlibs: fatal - %s has bad format\n", file_name); exit(-1); } insert_dependent_shlib(ptr2); } } dict[dict_size] = dependent_shlibs(); munmap(addr, size); close(fd); unlink(file_name); return dict[dict_size++]; } else { sprintf(file_name, "/tmp/apple_shlib_reg.%d", getpid()); close(1); if ( open(file_name, O_WRONLY | O_CREAT, 0) < 0 ) { exit(-1); } // Uncomment next 2 lines if it's needed to redirect stderr as well /* close(2); dup(1); */ /* aB. For some reason the file seems to be created with no read permission if done as a normal user */ chmod(file_name, S_IRUSR | S_IRGRP | S_IROTH); execl("/usr/bin/chatr", "chatr", path, 0); fprintf(stderr, "__objc_get_referenced_shlibs: failed to exec chatr\n"); exit(-1); } return res_nil; } static int _verbose = -1; static int _reg_mechanism = -1; #define OBJC_SHLIB_INIT_REGISTRATION if (_reg_mechanism == -1) {registration_init();} #define REG_METHOD_CHATR 0 #define REG_METHOD_DLD 1 static void registration_init() { const char *str_value = getenv("OBJC_SHOW_SHLIB_REGISTRATION"); if ( str_value ) { if ( !strcmp(str_value, "ALL") ) _verbose = 4; else if ( !strcmp(str_value, "LIBS") ) _verbose = 1; else if ( !strcmp(str_value, "LIST") ) _verbose = 2; else if ( !strcmp(str_value, "CTORS") ) _verbose = 3; else _verbose = 0; } else _verbose = 0; str_value = getenv("OBJC_SHLIB_REGISTRATION_METHOD"); if ( str_value ) { if ( !strcmp(str_value, "DLD") ) _reg_mechanism = REG_METHOD_DLD; else if ( !strcmp(str_value, "AB") ) _reg_mechanism = REG_METHOD_DLD; else if ( !strcmp(str_value, "NEW") ) _reg_mechanism = REG_METHOD_DLD; else _reg_mechanism = REG_METHOD_CHATR; } else _reg_mechanism = REG_METHOD_DLD; if (_verbose > 0) { if (_reg_mechanism == REG_METHOD_CHATR) { fprintf(stderr, "objc_hpux_register_shlib(): Using old (chatr) registration method\n"); } else { fprintf(stderr, "objc_hpux_register_shlib(): Using new (dld) registration method\n"); } } } static void insert_loaded_shlib(char *path); static int already_loaded(char *path) { static int first_time_here = 1; unsigned idx; char buf[256], *name; strcpy(buf, path); name = my_basename(buf); OBJC_SHLIB_INIT_REGISTRATION; for ( idx = 0; idx < _loaded_shlibs_count; idx++ ) { if ( ! strcmp(_loaded_shlibs[idx], name) ) { return 1; } } if ( first_time_here ) { // the root executable is the first shlib(sic) first_time_here = 0; insert_loaded_shlib(path); return 1; } return 0; } static void insert_loaded_shlib(char *path) { char **_loaded_shlibs_temp; char buf[256], *name; strcpy(buf, path); name = my_basename(buf); if ( already_loaded(path) ) { return; } if ( _loaded_shlibs_count >= _loaded_shlibs_size ) { _loaded_shlibs_temp = _loaded_shlibs; _loaded_shlibs_size += 32; _loaded_shlibs = (char **)_malloc_ptr(_loaded_shlibs_size*sizeof(char *)); if ( ! _loaded_shlibs ) { fprintf(stderr, "objc_hpux_register_shlib() - fatal: Failed to malloc _loaded_shlibs list. Exit\n"); exit(-1); } memcpy(_loaded_shlibs, _loaded_shlibs_temp, _loaded_shlibs_count); if ( _loaded_shlibs_temp != _loaded_shlibs_init ) { _free_ptr(_loaded_shlibs_temp); } } if ( ! (_loaded_shlibs[_loaded_shlibs_count] = _malloc_ptr(strlen(name)+1)) ) { fprintf(stderr, "objc_hpux_register_shlib() - fatal: Failed to malloc _loaded_shlibs entry. Exit\n"); exit(-1); } strcpy(_loaded_shlibs[_loaded_shlibs_count++], name); return; } static char *_pending_shlibs_init[128] = { "nhnd" }; static unsigned _pending_shlibs_size = 128; static unsigned _pending_shlibs_count = 1; static char **_pending_shlibs = _pending_shlibs_init; static void dump_pending_shlibs() { int i; printf("**** Pending shlibs ****\n"); for ( i=0; i<_pending_shlibs_count; i++ ) { printf("\t%s\n", _pending_shlibs[i]+sizeof(void*)); } printf("--- ----\n"); } static int already_pending(const char *path) { unsigned idx; for ( idx = 0; idx < _pending_shlibs_count; idx++ ) { if ( ! strcmp(_pending_shlibs[idx]+sizeof(void*), path) ) { if (_verbose > 1) { fprintf(stderr, "already_pending(): Already pended shlib %s\n", path); } return 1; } } if (_verbose > 1) { fprintf(stderr, "already_pending(): Pending shlib %s\n", path); } return 0; } static void insert_pending_shlib(struct shl_descriptor *desc) { char **_pending_shlibs_temp; char *ptr; int mask; if (_verbose > 1) { fprintf(stderr, "insert_pending_shlib(): Inserting shlib %s\n", desc->filename); } if ( already_pending(desc->filename) ) return; if ( _pending_shlibs_count >= _pending_shlibs_size ) { _pending_shlibs_temp = _pending_shlibs; _pending_shlibs_size += 32; _pending_shlibs = (char **)_malloc_ptr(_pending_shlibs_size*sizeof(char *)); if ( ! _pending_shlibs ) { fprintf(stderr, "objc_hpux_register_shlib() - fatal: Failed to malloc _pending_shlibs list. Exit\n"); exit(-1); } memcpy(_pending_shlibs, _pending_shlibs_temp, _pending_shlibs_count); if ( _pending_shlibs_temp != _pending_shlibs_init ) { _free_ptr(_pending_shlibs_temp); } } if ( ! (ptr = _malloc_ptr(strlen(desc->filename)+1+sizeof(void *)*2)) ) { fprintf(stderr, "objc_hpux_register_shlib() - fatal: Failed to malloc _pending_shlibs entry. Exit\n"); exit(-1); } strcpy(ptr+sizeof(void*), desc->filename); *(void **)ptr = desc->handle; _pending_shlibs[_pending_shlibs_count] = ptr; return; } static void delete_pending_shlib(const char *path) { unsigned idx; char *ptr; if (_verbose > 1) { fprintf(stderr, "delete_pending_shlib(): Deleting shlib %s\n", path); } for ( idx = 0; idx < _pending_shlibs_count; idx++ ) { ptr = _pending_shlibs[idx]+sizeof(void*); if ( ! strcmp(ptr, path) ) { if ( strcmp(ptr, "") ) { _free_ptr(_pending_shlibs[idx]); _pending_shlibs[idx] = ""; if (_verbose > 1) { fprintf(stderr, "delete_pending_shlib(): Found and deleted shlib %s\n", path); } } return; } } } static int more_pending_shlibs() { unsigned idx; char *ptr; for ( idx = 0; idx < _pending_shlibs_count; idx++ ) { ptr = _pending_shlibs[idx]+sizeof(void*); if ( strcmp(ptr, "") ) { return 0; } } if (_verbose > 1) { fprintf(stderr, "more_pending_shlib(): Pending shlibs remain\n"); } return 1; } static int dependencies_resolved(char *path) { char **referenced_shlibs; referenced_shlibs = __objc_get_referenced_shlibs(path); referenced_shlibs++; for ( ; strcmp(*referenced_shlibs, ""); referenced_shlibs++ ) { if ( !already_loaded(*referenced_shlibs) ) { if (_verbose > 1) { fprintf(stderr, "dependencies_resolved(): Dependencies remaining for shlib %s\n", path); } return 0; } } if (_verbose > 1) { fprintf(stderr, "dependencies_resolved(): Dependencies resolved for shlib %s\n", path); } return 1; } void objc_hpux_register_shlib_handle(void *handle); static void resolve_pending_shlibs() { char *ptr; unsigned idx; for ( idx = 0; idx < _pending_shlibs_count; idx++ ) { ptr = _pending_shlibs[idx]+sizeof(void*); if ( dependencies_resolved(ptr) ) { if ( _verbose >= 1 ) { fprintf(stderr, "resolve_pending_shlibs(): Examining shlib %s\n", ptr); } objc_hpux_register_shlib_handle(*(void **)_pending_shlibs[idx]); delete_pending_shlib(ptr); insert_loaded_shlib(ptr); } } } void objc_hpux_register_shlib_handle(void *handle) { extern void *CMH; extern objc_finish_header(); int isCMHReset; int sym_count, sym_idx; struct shl_symbol *symbols; // use malloc and not _malloc_ptr sym_count = shl_getsymbols(handle, TYPE_PROCEDURE, EXPORT_SYMBOLS, malloc, &symbols); if ( sym_count == -1 ) { fprintf(stderr, "objc_hpux_register_shlib_handle() - WARNING: shl_getsymbols failed. Continue at your own risk...\n"); //exit(-1); return; } isCMHReset = 0; for ( sym_idx = 0; sym_idx < sym_count; sym_idx++ ) { if ( !strncmp(symbols[sym_idx].name, "_GLOBAL_$I$", 11) ) { if ( ! isCMHReset ) { CMH = (void *)0; isCMHReset = 1; } if ( _verbose >= 3 ) fprintf(stderr, "objc_hpux_register_shlib_handle(): found ctor %s...\n", symbols[sym_idx].name); ((void (*)())(symbols[sym_idx].value))(); if ( _verbose >= 3 ) fprintf(stderr, "objc_hpux_register_shlib_handle(): ... and executed it\n"); } } if ( isCMHReset ) objc_finish_header(); // use free and not _free_ptr free(symbols); return; } void objc_hpux_register_shlib() { int idx; int registered_at_least_one_shlib; struct shl_descriptor desc; OBJC_SHLIB_INIT_REGISTRATION; if (_reg_mechanism != REG_METHOD_CHATR) return; if ( _verbose == 2 || _verbose == 4 ) fprintf(stderr, "---- ----\n"); registered_at_least_one_shlib = 0; for ( idx = 0; !shl_get_r(idx, &desc); idx++ ) { if ( already_loaded(desc.filename) ) { if ( _verbose == 2 || _verbose == 4 ) fprintf(stderr, "objc_hpux_register_shlib(): Skipping shlib %s\n", desc.filename); continue; } if ( !dependencies_resolved(desc.filename) ) { insert_pending_shlib(&desc); continue; } if ( _verbose >= 1 || _verbose == 4 ) fprintf(stderr, "objc_hpux_register_shlib(): Examining shlib %s\n", desc.filename); objc_hpux_register_shlib_handle(desc.handle); delete_pending_shlib(desc.filename); insert_loaded_shlib(desc.filename); registered_at_least_one_shlib = 1; } // This is the last call and the last chance to resolve them all! if ( ! registered_at_least_one_shlib ) { while ( more_pending_shlibs() ) resolve_pending_shlibs(); } if ( _verbose == 2 || _verbose == 4) fprintf(stderr, "---- ----\n\n"); return; } /* * An alternative, more efficient shlib registration that relies on the initializer * functions in each shlib being called in the correct order. This was initially deemed not to work. aB. */ void objc_hpux_register_named_shlib(const char *shlib_name) { int idx; struct shl_descriptor desc; char buf1[256], *p1; char buf2[256], *p2; OBJC_SHLIB_INIT_REGISTRATION; strcpy(buf1, shlib_name); p1 = my_basename(buf1); /* Do we use the new registration method or not ? */ if (_reg_mechanism == REG_METHOD_DLD) { if ( _verbose >= 1 ) { fprintf(stderr, "objc_hpux_register_named_shlib(): Registering shlib %s\n", shlib_name); } for ( idx = 0; !shl_get_r(idx, &desc); idx++ ) { strcpy(buf2, desc.filename); p2 = my_basename(buf2); /* Avoid registering the main executable (initializer == NULL) */ if ( strcmp(p1, p2) == 0 && desc.initializer != NULL) { objc_hpux_register_shlib_handle(desc.handle); if ( _verbose >= 1 ) { fprintf(stderr, "objc_hpux_register_named_shlib(): Registered shlib %s desc.initializer %x\n", desc.filename, desc.initializer); } break; } } } else { /* Just do things the old way */ objc_hpux_register_shlib(); } } /* Hardcoded in here for now as libpdo is built in a special manner */ void objc_hpux_register_shlib_pdo() { objc_hpux_register_named_shlib("libpdo.sl"); } unsigned __objc_msg_spew(unsigned self_obj, unsigned self_cls, unsigned addr) { fprintf(stderr, "\n\n**** __objc_msg_spew(self:0x%08x self->isa:0x%08x cls:0x%08x) ****\n\n", self_obj, *(unsigned *)self_obj, self_cls); return addr; }