#include <stdlib.h>
#include <string.h>
#include <mach-o/loader.h>
#include <mach-o/stab.h>
#ifdef __OPENSTEP__
#include <mach-o/rld.h>
#endif
#include "stuff/ofile.h"
#include "stuff/allocate.h"
#include "stuff/errors.h"
#include "gprof.h"
struct shlib_text_range *shlib_text_ranges = NULL;
uint32_t nshlib_text_ranges = 0;
static struct ofile ofile = { 0 };
#ifdef __OPENSTEP__
static uint32_t link_edit_address;
static uint32_t address_func(
uint32_t size,
uint32_t headers_size);
#endif
static void count_func_symbols(
struct nlist *symbols,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
uint32_t strsize);
static void load_func_symbols(
struct nlist *symbols,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
uint32_t strsize,
uint64_t vmaddr_slide);
static enum bool funcsymbol(
uint8_t n_type,
uint8_t n_sect,
char *name);
static int valcmp(
nltype *p1,
nltype *p2);
static void count_N_SO_stabs(
struct nlist *symbols,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
uint32_t strsize);
static void load_files(
struct nlist *symbols,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
uint32_t strsize);
static struct arch_flag host_arch_flag;
void
getnfile(
void)
{
uint32_t i, j, ncmds;
uint64_t text_highpc;
struct load_command *lc;
struct segment_command *sg;
struct segment_command_64 *sg64;
struct section *s;
struct section_64 *s64;
struct symtab_command *st;
struct nlist *symbols;
struct nlist_64 *symbols64;
#ifdef notdef
struct ofile lib_ofile;
uint32_t k;
struct fvmlib_command *fl;
struct load_command *lib_lc;
struct symtab_command *lib_st;
char *lib_name;
#endif
nname = 0;
n_files = 0;
if(get_arch_from_host(&host_arch_flag, NULL) == 0)
fatal("can't determine the host architecture");
if(ofile_map(a_outname, NULL, NULL, &ofile, FALSE) == FALSE)
return;
if(ofile.file_type == OFILE_FAT){
(void)ofile_first_arch(&ofile);
do{
if(host_arch_flag.cputype == ofile.mh_cputype)
goto good;
}while(ofile_next_arch(&ofile) == TRUE);
(void)ofile_first_arch(&ofile);
do{
if((host_arch_flag.cputype | CPU_ARCH_ABI64) ==
ofile.mh_cputype){
host_arch_flag.cputype |= CPU_ARCH_ABI64;
goto good;
}
}while(ofile_next_arch(&ofile) == TRUE);
error("file: %s does not contain the host architecture", a_outname);
return;
}
else if(ofile.file_type == OFILE_ARCHIVE){
error("file: %s is not an Mach-O file executable", a_outname);
return;
}
else if(ofile.file_type == OFILE_Mach_O){
if(host_arch_flag.cputype == ofile.mh_cputype)
goto good;
if((host_arch_flag.cputype | CPU_ARCH_ABI64) == ofile.mh_cputype){
host_arch_flag.cputype |= CPU_ARCH_ABI64;
goto good;
}
error("file: %s is not of the host architecture", a_outname);
return;
}
else{
error("file: %s is not an Mach-O file executable", a_outname);
return;
}
good:
if((ofile.mh == NULL && ofile.mh64 == NULL) ||
(ofile.mh_filetype != MH_EXECUTE &&
ofile.mh_filetype != MH_DYLIB &&
ofile.mh_filetype != MH_DYLINKER))
fatal("file: %s is not a Mach-O executable file", a_outname);
st = NULL;
lc = ofile.load_commands;
if(ofile.mh != NULL){
text_highpc = 0xfffffffe;
ncmds = ofile.mh->ncmds;
}
else{
text_highpc = 0xfffffffffffffffeULL;
ncmds = ofile.mh64->ncmds;
}
for(i = 0; i < ncmds; i++){
if(lc->cmd == LC_SEGMENT){
sg = (struct segment_command *)lc;
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if(strcmp(s->sectname, SECT_TEXT) == 0 &&
strcmp(s->segname, SEG_TEXT) == 0){
textspace = (unsigned char *)ofile.object_addr +
s->offset;
text_highpc = s->addr + s->size;
}
s++;
}
}
else if(lc->cmd == LC_SEGMENT_64){
sg64 = (struct segment_command_64 *)lc;
s64 = (struct section_64 *)
((char *)sg64 + sizeof(struct segment_command_64));
for(j = 0; j < sg64->nsects; j++){
if(strcmp(s64->sectname, SECT_TEXT) == 0 &&
strcmp(s64->segname, SEG_TEXT) == 0){
textspace = (unsigned char *)ofile.object_addr +
s64->offset;
text_highpc = s64->addr + s64->size;
}
s64++;
}
}
#ifdef notdef
else if(lc->cmd == LC_LOADFVMLIB){
fl = (struct fvmlib_command *)lc;
lib_name = (char *)fl + fl->fvmlib.name.offset;
if(ofile_map(lib_name, &host_arch_flag, NULL, &lib_ofile,
FALSE) == FALSE)
goto done1;
if(lib_ofile.mh == NULL || lib_ofile.mh->filetype != MH_FVMLIB){
warning("file: %s is not a shared library file", lib_name);
goto done_and_unmap1;
}
lib_st = NULL;
lib_lc = lib_ofile.load_commands;
for(j = 0; j < lib_ofile.mh->ncmds; j++){
if(lib_st == NULL && lib_lc->cmd == LC_SYMTAB){
lib_st = (struct symtab_command *)lib_lc;
count_func_symbols((struct nlist *)
(lib_ofile.object_addr + lib_st->symoff),
lib_st->nsyms,
lib_ofile.object_addr + lib_st->stroff,
lib_st->strsize);
break;
}
else if(lib_lc->cmd == LC_SEGMENT){
sg = (struct segment_command *)lib_lc;
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(k = 0; k < sg->nsects; k++){
if(strcmp(s->sectname, SECT_TEXT) == 0 &&
strcmp(s->segname, SEG_TEXT) == 0){
shlib_text_ranges =
reallocate(shlib_text_ranges,
(nshlib_text_ranges + 1) *
sizeof(struct shlib_text_range));
shlib_text_ranges[nshlib_text_ranges].lowpc =
s->addr;
shlib_text_ranges[nshlib_text_ranges].highpc =
s->addr + s->size;
nshlib_text_ranges++;
}
s++;
}
}
lib_lc = (struct load_command *)
((char *)lib_lc + lib_lc->cmdsize);
}
done_and_unmap1:
ofile_unmap(&lib_ofile);
done1: ;
}
#endif
else if(st == NULL && lc->cmd == LC_SYMTAB){
st = (struct symtab_command *)lc;
if(ofile.mh != NULL){
symbols = (struct nlist *)
(ofile.object_addr + st->symoff);
symbols64 = NULL;
}
else{
symbols64 = (struct nlist_64 *)
(ofile.object_addr + st->symoff);
symbols = NULL;
}
count_func_symbols(symbols, symbols64, st->nsyms,
ofile.object_addr + st->stroff, st->strsize);
count_N_SO_stabs(symbols, symbols64, st->nsyms,
ofile.object_addr + st->stroff, st->strsize);
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(nname == 0)
fatal("executable file %s: has no symbols", a_outname);
nl = (nltype *)calloc(nname + 2, sizeof(nltype));
if(nl == NULL)
fatal("No room for %lu bytes of symbol table\n",
(nname + 2) * sizeof(nltype));
npe = nl;
files = (struct file *)calloc(n_files/2, sizeof(struct file));
if(files == NULL)
fatal("No room for %lu bytes of file table\n",
n_files/2 * sizeof(struct file));
n_files = 0;
if(st != NULL){
if(ofile.mh != NULL){
symbols = (struct nlist *)
(ofile.object_addr + st->symoff);
symbols64 = NULL;
}
else{
symbols64 = (struct nlist_64 *)
(ofile.object_addr + st->symoff);
symbols = NULL;
}
load_func_symbols(symbols, symbols64, st->nsyms,
ofile.object_addr + st->stroff, st->strsize, 0);
load_files(symbols, symbols64, st->nsyms,
ofile.object_addr + st->stroff, st->strsize);
}
#ifdef notdef
lc = ofile.load_commands;
for(i = 0; i < ofile.mh->ncmds; i++){
if(lc->cmd == LC_LOADFVMLIB){
fl = (struct fvmlib_command *)lc;
lib_name = (char *)fl + fl->fvmlib.name.offset;
if(ofile_map(lib_name, &host_arch_flag, NULL, &lib_ofile,
FALSE) == TRUE){
lib_st = NULL;
lib_lc = lib_ofile.load_commands;
for(j = 0; j < lib_ofile.mh->ncmds; j++){
lib_lc = (struct load_command *)
((char *)lib_lc + lib_lc->cmdsize);
if(lib_st == NULL && lib_lc->cmd == LC_SYMTAB){
lib_st = (struct symtab_command *)lib_lc;
load_func_symbols((struct nlist *)
(lib_ofile.object_addr + lib_st->symoff),
lib_st->nsyms,
lib_ofile.object_addr + lib_st->stroff,
lib_st->strsize, 0);
break;
}
}
}
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
#endif
npe->value = text_highpc;
npe->name = "past end of text";
npe++;
nname++;
if(ofile.mh != NULL)
npe->value = npe->svalue = 0xffffffff;
else
npe->value = npe->svalue = 0xffffffffffffffffULL;
npe->name = "top of memory";
qsort(nl, nname, sizeof(nltype),
(int (*)(const void *, const void *))valcmp);
#ifdef DEBUG
if(debug & AOUTDEBUG){
for(i = 0; i < nname; i++){
printf("[getnfile] ");
if(ofile.mh != NULL)
printf("0x%08x", (unsigned int)nl[i].value);
else
printf("%016llx", nl[i].value);
printf("\t%s\n", nl[i].name);
}
}
#endif
}
#ifdef __OPENSTEP__
void
get_rld_state_symbols(
void)
{
uint32_t i, j, save_nname;
NXStream *stream;
struct mach_header **headers;
char *object_addr;
struct load_command *lc;
struct symtab_command *st;
if(grld_nloaded_states == 0)
return;
headers = allocate(grld_nloaded_states * sizeof(struct mach_header *));
stream = NXOpenFile(fileno(stdout), NX_WRITEONLY);
if(rld_load_basefile(stream, a_outname) == 0){
NXFlush(stream);
fflush(stdout);
fatal("can't load: %s as base file", a_outname);
}
for(i = 0; i < grld_nloaded_states; i++){
link_edit_address = (uint32_t)grld_loaded_state[i].header_addr;
rld_address_func(address_func);
if(rld_load(stream, &(headers[i]),
grld_loaded_state[i].object_filenames,
RLD_DEBUG_OUTPUT_FILENAME) == 0){
NXFlush(stream);
fflush(stdout);
fatal("rld_load() failed");
}
}
save_nname = nname;
for(i = 0; i < grld_nloaded_states; i++){
st = NULL;
object_addr = (char *)headers[i];
lc = (struct load_command *)
(object_addr + sizeof(struct mach_header));
for(j = 0; j < headers[i]->ncmds; j++){
if(st == NULL && lc->cmd == LC_SYMTAB){
st = (struct symtab_command *)lc;
count_func_symbols((struct nlist *)
(object_addr + st->symoff),
st->nsyms,
object_addr + st->stroff,
st->strsize);
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
}
nl = (nltype *)realloc(nl, (nname + 1) * sizeof(nltype));
if(nl == NULL)
fatal("No room for %lu bytes of symbol table\n",
(nname + 1) * sizeof(nltype));
npe = nl + (save_nname + 1);
memset(npe, '\0', (nname - save_nname) * sizeof(nltype));
for(i = 0; i < grld_nloaded_states; i++){
st = NULL;
object_addr = (char *)headers[i];
lc = (struct load_command *)
(object_addr + sizeof(struct mach_header));
for(j = 0; j < headers[i]->ncmds; j++){
if(st == NULL && lc->cmd == LC_SYMTAB){
st = (struct symtab_command *)lc;
load_func_symbols((struct nlist *)
(object_addr + st->symoff),
st->nsyms,
object_addr + st->stroff,
st->strsize,
0);
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
}
#ifdef DEBUG
if(debug & RLDDEBUG){
for(i = save_nname + 1; i < nname + 1; i++){
printf("[get_rld_state_symbols] 0x%08x\t%s\n",
(unsigned int)nl[i].value, nl[i].name);
}
}
#endif
qsort(nl, nname + 1, sizeof(nltype),
(int (*)(const void *, const void *))valcmp);
free(headers);
}
static
uint32_t
address_func(
uint32_t size,
uint32_t headers_size)
{
return(link_edit_address);
}
#endif
void
get_dyld_state_symbols(
void)
{
uint32_t i, j, save_nname, ncmds;
struct ofile *ofiles;
struct load_command *lc;
struct segment_command *sg;
struct segment_command_64 *sg64;
struct symtab_command *st;
struct nlist *symbols;
struct nlist_64 *symbols64;
if(image_count == 0)
return;
ofiles = allocate(image_count * sizeof(struct ofile));
for(i = 0; i < image_count; i++){
if(ofile_map(dyld_images[i].name, &host_arch_flag, NULL, ofiles + i,
FALSE) == 0)
fatal("ofile_map() failed");
if(ofiles[i].mh == NULL && ofiles[i].mh64 == NULL)
fatal("file from dyld loaded state: %s is not a Mach-O file",
dyld_images[i].name);
}
save_nname = nname;
for(i = 0; i < image_count; i++){
st = NULL;
lc = ofiles[i].load_commands;
if(ofiles[i].mh != NULL)
ncmds = ofiles[i].mh->ncmds;
else
ncmds = ofiles[i].mh64->ncmds;
for(j = 0; j < ncmds; j++){
if(st == NULL && lc->cmd == LC_SYMTAB){
st = (struct symtab_command *)lc;
if(ofiles[i].mh != NULL){
symbols = (struct nlist *)
(ofiles[i].object_addr + st->symoff);
symbols64 = NULL;
}
else{
symbols64 = (struct nlist_64 *)
(ofiles[i].object_addr + st->symoff);
symbols = NULL;
}
count_func_symbols(symbols, symbols64, st->nsyms,
ofiles[i].object_addr + st->stroff,
st->strsize);
}
if(dyld_images[i].image_header != 0 &&
dyld_images[i].vmaddr_slide == 0){
if(lc->cmd == LC_SEGMENT){
sg = (struct segment_command *)lc;
if(sg->filesize != 0){
dyld_images[i].vmaddr_slide =
dyld_images[i].image_header - sg->vmaddr;
}
}
else if(lc->cmd == LC_SEGMENT_64){
sg64 = (struct segment_command_64 *)lc;
if(sg64->filesize != 0){
dyld_images[i].vmaddr_slide =
dyld_images[i].image_header - sg64->vmaddr;
}
}
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
}
nl = (nltype *)realloc(nl, (nname + 1) * sizeof(nltype));
if(nl == NULL)
fatal("No room for %lu bytes of symbol table\n",
(nname + 1) * sizeof(nltype));
npe = nl + (save_nname + 1);
memset(npe, '\0', (nname - save_nname) * sizeof(nltype));
for(i = 0; i < image_count; i++){
st = NULL;
lc = ofiles[i].load_commands;
if(ofiles[i].mh != NULL)
ncmds = ofiles[i].mh->ncmds;
else
ncmds = ofiles[i].mh64->ncmds;
for(j = 0; j < ncmds; j++){
if(st == NULL && lc->cmd == LC_SYMTAB){
st = (struct symtab_command *)lc;
if(ofiles[i].mh != NULL){
symbols = (struct nlist *)
(ofiles[i].object_addr + st->symoff);
symbols64 = NULL;
}
else{
symbols64 = (struct nlist_64 *)
(ofiles[i].object_addr + st->symoff);
symbols = NULL;
}
load_func_symbols(symbols, symbols64, st->nsyms,
ofiles[i].object_addr + st->stroff,
st->strsize,
dyld_images[i].vmaddr_slide);
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
}
#ifdef DEBUG
if(debug & DYLDDEBUG){
for(i = save_nname + 1; i < nname + 1; i++){
printf("[get_dyld_state_symbols] ");
if(ofiles[0].mh != NULL)
printf("0x%08x", (unsigned int)nl[i].value);
else
printf("%016llx", nl[i].value);
printf("\t%s\n", nl[i].name);
}
}
#endif
qsort(nl, nname + 1, sizeof(nltype),
(int (*)(const void *, const void *))valcmp);
free(ofiles);
}
static
void
count_func_symbols(
struct nlist *symbols,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
uint32_t strsize)
{
uint32_t i;
uint32_t n_strx;
uint8_t n_type;
uint8_t n_sect;
for(i = 0; i < nsymbols; i++){
if(symbols != NULL){
n_strx = symbols[i].n_un.n_strx;
n_type = symbols[i].n_type;
n_sect = symbols[i].n_sect;
}
else{
n_strx = symbols64[i].n_un.n_strx;
n_type = symbols64[i].n_type;
n_sect = symbols64[i].n_sect;
}
if(n_strx != 0 && n_strx < strsize){
if(funcsymbol(n_type, n_sect, strings + n_strx))
nname++;
}
}
}
static
void
load_func_symbols(
struct nlist *symbols,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
uint32_t strsize,
uint64_t vmaddr_slide)
{
uint32_t i;
uint32_t n_strx;
uint8_t n_type;
uint8_t n_sect;
uint64_t n_value;
for(i = 0; i < nsymbols; i++){
if(symbols != NULL){
n_strx = symbols[i].n_un.n_strx;
n_type = symbols[i].n_type;
n_sect = symbols[i].n_sect;
n_value = symbols[i].n_value;
}
else{
n_strx = symbols64[i].n_un.n_strx;
n_type = symbols64[i].n_type;
n_sect = symbols64[i].n_sect;
n_value = symbols64[i].n_value;
}
if(n_strx != 0 && n_strx < strsize){
if(funcsymbol(n_type, n_sect, strings + n_strx)){
npe->value = n_value + vmaddr_slide;
npe->name = strings + n_strx;
npe++;
}
}
}
}
static
enum bool
funcsymbol(
uint8_t n_type,
uint8_t n_sect,
char *name)
{
int type;
if(n_type & N_STAB)
return(FALSE);
type = n_type & N_TYPE;
if(type == N_SECT && n_sect == 1)
type = N_TEXT;
if(type != N_TEXT)
return FALSE;
if((!(n_type & N_EXT)) && aflag)
return(FALSE);
for( ; *name ; name += 1 ){
if(*name == '.' || *name == '$'){
return(FALSE);
}
}
return(TRUE);
}
static
int
valcmp(
nltype *p1,
nltype *p2)
{
if(p1->value < p2->value){
return(LESSTHAN);
}
if(p1->value > p2->value){
return(GREATERTHAN);
}
return(EQUALTO);
}
static
void
count_N_SO_stabs(
struct nlist *symbols,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
uint32_t strsize)
{
uint32_t i, len;
char *name;
uint32_t n_strx;
uint8_t n_type;
uint64_t n_value;
for(i = 0; i < nsymbols; i++){
if(symbols != NULL){
n_strx = symbols[i].n_un.n_strx;
n_type = symbols[i].n_type;
n_value = symbols[i].n_value;
}
else{
n_strx = symbols64[i].n_un.n_strx;
n_type = symbols64[i].n_type;
n_value = symbols64[i].n_value;
}
if(n_type == N_SO){
if(n_strx != 0 && n_strx < strsize){
name = strings + n_strx;
len = strlen(name);
if(len != 0 && name[len-1] == '/')
continue;
}
n_files++;
}
}
}
static
void
load_files(
struct nlist *symbols,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
uint32_t strsize)
{
uint32_t i;
char *s, *name;
int len;
int oddeven;
uint32_t n_strx;
uint8_t n_type;
uint64_t n_value;
oddeven = 0;
for(i = 0; i < nsymbols; i++){
if(symbols != NULL){
n_strx = symbols[i].n_un.n_strx;
n_type = symbols[i].n_type;
n_value = symbols[i].n_value;
}
else{
n_strx = symbols64[i].n_un.n_strx;
n_type = symbols64[i].n_type;
n_value = symbols64[i].n_value;
}
if(n_type == N_SO){
if(n_strx != 0 && n_strx < strsize){
name = strings + n_strx;
len = strlen(name);
if(len != 0 && name[len-1] == '/')
continue;
}
if(oddeven){
files[n_files++].lastpc = n_value;
oddeven = 0;
}
else {
s = strings + n_strx;
len = strlen(s);
if(len > 0)
s[len-1] = 'o';
files[n_files].name = files[n_files].what_name = s;
files[n_files].firstpc = n_value;
oddeven = 1;
}
}
}
#ifdef notdef
for(i = 0; i < n_files; i++){
fprintf(stderr, "files[%lu] firstpc = 0x%x name = %s\n", i,
(unsigned int)(files[i].firstpc), files[i].name);
}
#endif
}
void
get_text_min_max(
uint64_t *text_min,
uint64_t *text_max)
{
uint32_t i, j, ncmds;
struct load_command *lc;
struct segment_command *sg;
struct segment_command_64 *sg64;
struct section *s;
struct section_64 *s64;
*text_min = 0;
if(ofile.mh != NULL){
*text_max = 0xffffffff;
ncmds = ofile.mh->ncmds;
}
else{
*text_max = 0xffffffffffffffffULL;
ncmds = ofile.mh64->ncmds;
}
lc = ofile.load_commands;
for (i = 0; i < ncmds; i++){
if(lc->cmd == LC_SEGMENT){
sg = (struct segment_command *)lc;
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
if(strcmp(s->sectname, SECT_TEXT) == 0 &&
strcmp(s->segname, SEG_TEXT) == 0){
*text_min = s->addr;
*text_max = s->addr + s->size;
return;
}
s++;
}
}
else if(lc->cmd == LC_SEGMENT_64){
sg64 = (struct segment_command_64 *)lc;
s64 = (struct section_64 *)
((char *)sg64 + sizeof(struct segment_command_64));
for(j = 0; j < sg64->nsects; j++){
if(strcmp(s64->sectname, SECT_TEXT) == 0 &&
strcmp(s64->segname, SEG_TEXT) == 0){
*text_min = s64->addr;
*text_max = s64->addr + s64->size;
return;
}
s++;
}
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
}