#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "stuff/ofile.h"
#include "stuff/errors.h"
#include "stuff/seg_addr_table.h"
struct check_block {
char *install_name;
enum bool check_result;
struct seg_addr_table *entry;
};
char *progname = NULL;
static void usage(
void);
static void check_for_install_name(
struct ofile *ofile,
char *arch_name,
void *cookie);
static void check_for_addresses(
struct ofile *ofile,
char *arch_name,
void *cookie);
extern char apple_version[];
char *version = apple_version;
int
main(
int argc,
char **argv,
char **envp)
{
int i;
uint32_t table_size;
char *install_name, *image_file_name, *seg_addr_table_name,
*seg_addr_table_filename;
struct check_block block;
struct seg_addr_table *seg_addr_table, *entry;
progname = argv[0];
install_name = NULL;
image_file_name = NULL;
seg_addr_table = NULL;
seg_addr_table_filename = NULL;
for(i = 1; i < argc; i++){
if(argv[i][0] == '-'){
if(strcmp(argv[i], "-install_name") == 0){
if(i + 1 == argc){
error("missing argument(s) to %s option", argv[i]);
usage();
}
if(install_name != NULL){
error("more than one: %s option", argv[i]);
usage();
}
install_name = argv[i+1];
i++;
}
else if(strcmp(argv[i], "-seg_addr_table") == 0){
if(i + 1 == argc){
error("missing argument(s) to %s option", argv[i]);
usage();
}
if(seg_addr_table != NULL){
error("more than one: %s option", argv[i]);
usage();
}
seg_addr_table_name = argv[i+1];
seg_addr_table = parse_seg_addr_table(argv[i+1],
argv[i], argv[i+1], &table_size);
i++;
}
else if(strcmp(argv[i], "-seg_addr_table_filename") == 0){
if(i + 1 == argc){
error("missing argument(s) to %s option", argv[i]);
usage();
}
if(seg_addr_table_filename != NULL){
error("more than one: %s option", argv[i]);
usage();
}
seg_addr_table_filename = argv[i+1];
i++;
}
else{
error("unknown option %s\n", argv[i]);
usage();
}
}
else{
if(image_file_name != NULL){
error("more than file name specified (%s and %s)",
image_file_name, argv[i]);
usage();
}
image_file_name = argv[i];
}
}
if(image_file_name == NULL){
error("must specify a file name to be checked");
usage();
}
if(install_name == NULL){
error("must specify the -install_name <install_name> option");
usage();
}
if(seg_addr_table == NULL){
error("must specify the -seg_addr_table <table_name> option");
usage();
}
if(seg_addr_table_filename == NULL){
error("must specify the -seg_addr_table_filename <pathname> " "option");
usage();
}
block.install_name = install_name;
block.check_result = TRUE;
ofile_process(image_file_name, NULL, 0, TRUE,
TRUE, TRUE, FALSE, check_for_install_name, &block);
if(block.check_result == FALSE)
return(2);
entry = search_seg_addr_table(seg_addr_table, seg_addr_table_filename);
if(entry == NULL)
return(3);
block.entry = entry;
block.check_result = TRUE;
ofile_process(image_file_name, NULL, 0, TRUE,
TRUE, TRUE, FALSE, check_for_addresses, &block);
if(block.check_result == FALSE)
return(4);
if((entry->split == FALSE && entry->seg1addr == 0) ||
(entry->split == TRUE && (entry->segs_read_only_addr == 0 ||
entry->segs_read_write_addr == 0)) )
return(5);
return(EXIT_SUCCESS);
}
static
void
usage(
void)
{
fprintf(stderr, "Usage: %s <file_name> -install_name <install_name> "
"-seg_addr_table <table_name> -seg_addr_table_filename "
"<path_name>\n", progname);
exit(EXIT_FAILURE);
}
static
void
check_for_install_name(
struct ofile *ofile,
char *arch_name,
void *cookie)
{
uint32_t i;
struct check_block *block;
struct load_command *lc;
struct dylib_command *dl;
char *name;
#ifdef DEBUG
printf("In check_for_install_name() ofile->file_name = %s",
ofile->file_name);
if(arch_name != NULL)
printf(" arch_name = %s\n", arch_name);
else
printf("\n");
#endif
block = (struct check_block *)cookie;
if(ofile->mh == NULL){
block->check_result = FALSE;
return;
}
lc = ofile->load_commands;
for(i = 0; i < ofile->mh->ncmds; i++){
if(lc->cmd == LC_ID_DYLIB){
dl = (struct dylib_command *)lc;
name = (char *)lc + dl->dylib.name.offset;
if(strncmp(name, "@executable_path",
sizeof("@executable_path") - 1) == 0)
return;
if(strcmp(name, block->install_name) != 0)
block->check_result = FALSE;
return;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
block->check_result = FALSE;
return;
}
static
void
check_for_addresses(
struct ofile *ofile,
char *arch_name,
void *cookie)
{
uint32_t i, segs_read_only_addr, segs_read_write_addr;
struct load_command *lc;
struct segment_command *sg, *first;
enum bool split;
struct check_block *block;
#ifdef DEBUG
printf("In check_for_addresses() ofile->file_name = %s",
ofile->file_name);
if(arch_name != NULL)
printf(" arch_name = %s\n", arch_name);
else
printf("\n");
#endif
block = (struct check_block *)cookie;
if(ofile->mh == NULL){
block->check_result = FALSE;
return;
}
split = (ofile->mh->flags & MH_SPLIT_SEGS) == MH_SPLIT_SEGS;
if(block->entry->split != split){
block->check_result = FALSE;
return;
}
lc = ofile->load_commands;
first = NULL;
segs_read_only_addr = UINT_MAX;
segs_read_write_addr = UINT_MAX;
for(i = 0; i < ofile->mh->ncmds; i++){
if(lc->cmd == LC_SEGMENT){
sg = (struct segment_command *)lc;
if(first == NULL){
first = sg;
if(split == FALSE &&
first->vmaddr != block->entry->seg1addr){
block->check_result = FALSE;
return;
}
}
if((sg->initprot & VM_PROT_WRITE) == 0){
if(split == TRUE && sg->vmaddr < segs_read_only_addr)
segs_read_only_addr = sg->vmaddr;
}
else{
if(split == TRUE && sg->vmaddr < segs_read_write_addr)
segs_read_write_addr = sg->vmaddr;
}
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(split == TRUE){
if(segs_read_only_addr != block->entry->segs_read_only_addr ||
segs_read_write_addr != block->entry->segs_read_write_addr){
block->check_result = FALSE;
return;
}
}
}