#import <stdio.h>
#import <stdlib.h>
#import "stuff/ofile.h"
#import "stuff/errors.h"
#import "stuff/bytesex.h"
#import "stuff/allocate.h"
__private_extern__ char *progname = NULL;
static char *old_dylib = NULL;
static char *new_dylib = NULL;
static enum bool arch_processed = FALSE;
static char *arch_name_being_processed = NULL;
static enum bool compatible = TRUE;
static enum byte_sex host_byte_sex = UNKNOWN_BYTE_SEX;
static char *strings = NULL;
static struct nlist *symbols = NULL;
static struct nlist_64 *symbols64 = NULL;
static void process_old(
struct ofile *ofile,
char *arch_name,
void *cookie);
static void compare(
struct ofile *new_ofile,
char *arch_name,
void *cookie);
static void check_dylib(
struct ofile *ofile,
char *arch_name);
static void check_global_symbols(
struct ofile *new_ofile,
struct ofile *old_ofile,
char *arch_name,
enum bool new_api_allowed);
static int dylib_bsearch(
const char *symbol_name,
const struct dylib_table_of_contents *toc);
static int dylib_bsearch64(
const char *symbol_name,
const struct dylib_table_of_contents *toc);
static int nlist_bsearch(
const char *symbol_name,
const struct nlist *sym);
static int nlist_bsearch64(
const char *symbol_name,
const struct nlist_64 *sym64);
extern char apple_version[];
char *version = apple_version;
int
main(
int argc,
char *argv[],
char *envp[])
{
progname = argv[0];
host_byte_sex = get_host_byte_sex();
if(argc != 3){
fprintf(stderr, "Usage: %s old_dylib new_dylib\n", progname);
exit(EXIT_FAILURE);
}
old_dylib = argv[1];
new_dylib = argv[2];
ofile_process(old_dylib, NULL, 0, TRUE, TRUE, TRUE, FALSE, process_old,
NULL);
if(compatible == TRUE)
return(EXIT_SUCCESS);
else
return(EXIT_FAILURE);
}
static
void
process_old(
struct ofile *old_ofile,
char *arch_name,
void *cookie)
{
struct arch_flag arch_flag;
check_dylib(old_ofile, arch_name);
arch_flag.name = (char *)get_arch_name_from_types(
old_ofile->mh_cputype,
old_ofile->mh_cpusubtype);
arch_flag.cputype = old_ofile->mh_cputype;
arch_flag.cpusubtype = old_ofile->mh_cpusubtype;
arch_processed = FALSE;
arch_name_being_processed = arch_name;
ofile_process(new_dylib, &arch_flag, 1, FALSE, TRUE, TRUE, FALSE,
compare, old_ofile);
if(arch_processed == FALSE)
fatal("new dynamic shared library: %s does not contain "
"architecture %s\n", new_dylib, arch_flag.name);
}
static
void
compare(
struct ofile *new_ofile,
char *arch_name,
void *cookie)
{
uint32_t i;
struct load_command *lc;
struct ofile *old_ofile;
struct dylib_command *old_dl, *new_dl;
char *old_install_name, *new_install_name;
enum bool new_api_allowed;
uint32_t ncmds;
arch_name = arch_name_being_processed;
check_dylib(new_ofile, arch_name);
old_ofile = (struct ofile *)cookie;
old_dl = NULL;
lc = old_ofile->load_commands;
if(old_ofile->mh != NULL)
ncmds = old_ofile->mh->ncmds;
else
ncmds = old_ofile->mh64->ncmds;
for(i = 0; i < ncmds; i++){
if(old_dl == NULL && lc->cmd == LC_ID_DYLIB){
old_dl = (struct dylib_command *)lc;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(old_dl == NULL){
if(arch_name != NULL)
fatal("malformed dynamic shared library: %s (for architecture "
"%s) (has no dylib id command)", old_dylib, arch_name);
else
fatal("malformed dynamic shared library: %s (has no dylib id "
"command)", old_dylib);
}
old_install_name = (char *)old_dl + old_dl->dylib.name.offset;
if(old_dl->dylib.current_version <
old_dl->dylib.compatibility_version){
if(arch_name != NULL)
fatal("malformed dynamic shared library: %s (for architecture "
"%s) (current version less than compatibility_version)",
old_dylib, arch_name);
else
fatal("malformed dynamic shared library: %s (current version "
"less than compatibility_version)", old_dylib);
}
new_dl = NULL;
lc = new_ofile->load_commands;
if(new_ofile->mh != NULL)
ncmds = new_ofile->mh->ncmds;
else
ncmds = new_ofile->mh64->ncmds;
for(i = 0; i < ncmds; i++){
if(new_dl == NULL && lc->cmd == LC_ID_DYLIB){
new_dl = (struct dylib_command *)lc;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(new_dl == NULL){
if(arch_name != NULL)
fatal("malformed dynamic shared library: %s (for architecture "
"%s) (has no dylib id command)", new_dylib, arch_name);
else
fatal("malformed dynamic shared library: %s (has no dylib id "
"command)", new_dylib);
}
new_install_name = (char *)new_dl + new_dl->dylib.name.offset;
if(new_dl->dylib.current_version <
new_dl->dylib.compatibility_version){
if(arch_name != NULL)
fatal("malformed dynamic shared library: %s (for architecture "
"%s) (current version less than compatibility_version)",
new_dylib, arch_name);
else
fatal("malformed dynamic shared library: %s (current version "
"less than compatibility_version)", new_dylib);
}
if(strcmp(old_install_name, new_install_name) != 0){
if(arch_name != NULL)
printf("For architecture %s ", arch_name);
printf("dynamic shared libraries have different install names (%s "
"and %s)\n", old_install_name, new_install_name);
compatible = FALSE;
}
if(old_dl->dylib.current_version >
new_dl->dylib.current_version){
if(arch_name != NULL)
printf("For architecture %s ", arch_name);
printf("current version of old dynamic shared library (%u) "
"greater than new dynamic shared library (%u)\n",
old_dl->dylib.current_version,new_dl->dylib.current_version);
compatible = FALSE;
}
if(old_dl->dylib.compatibility_version >
new_dl->dylib.compatibility_version){
if(arch_name != NULL)
printf("For architecture %s ", arch_name);
printf("compatibility version of old dynamic shared library (%u) "
"greater than new dynamic shared library (%u)\n",
old_dl->dylib.compatibility_version,
new_dl->dylib.compatibility_version);
compatible = FALSE;
new_api_allowed = TRUE;
}
else{
if(new_dl->dylib.compatibility_version !=
old_dl->dylib.compatibility_version)
new_api_allowed = TRUE;
else
new_api_allowed = FALSE;
}
check_global_symbols(new_ofile, old_ofile, arch_name, new_api_allowed);
arch_processed = TRUE;
}
static
void
check_dylib(
struct ofile *ofile,
char *arch_name)
{
if(ofile->file_type == OFILE_FAT){
if(ofile->arch_type != OFILE_Mach_O ||
(ofile->mh_filetype != MH_DYLIB &&
ofile->mh_filetype != MH_DYLIB_STUB))
fatal("for architecture %s file: %s is not a dynamic shared "
"library", ofile->arch_flag.name, ofile->file_name);
}
else
if(ofile->file_type != OFILE_Mach_O ||
(ofile->mh_filetype != MH_DYLIB &&
ofile->mh_filetype != MH_DYLIB_STUB))
fatal("file: %s is not a dynamic shared library",
ofile->file_name);
}
static
void
check_global_symbols(
struct ofile *new_ofile,
struct ofile *old_ofile,
char *arch_name,
enum bool new_api_allowed)
{
uint32_t i;
struct load_command *lc;
enum bool new_api, missing_symbols, found;
struct symtab_command *old_st, *new_st;
struct dysymtab_command *old_dyst, *new_dyst;
struct nlist *old_symbols, *new_symbols, *sym;
struct nlist_64 *old_symbols64, *new_symbols64, *sym64;
char *old_strings, *new_strings;
struct dylib_table_of_contents *old_tocs, *new_tocs, *toc;
char *symbol_name;
uint32_t ncmds, n_strx;
old_st = NULL;
old_dyst = NULL;
lc = old_ofile->load_commands;
if(old_ofile->mh != NULL)
ncmds = old_ofile->mh->ncmds;
else
ncmds = old_ofile->mh64->ncmds;
for(i = 0; i < ncmds; i++){
if(old_st == NULL && lc->cmd == LC_SYMTAB){
old_st = (struct symtab_command *)lc;
}
else if(old_dyst == NULL && lc->cmd == LC_DYSYMTAB){
old_dyst = (struct dysymtab_command *)lc;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(old_st == NULL || old_st->nsyms == 0){
if(arch_name != NULL)
fatal("old dynamic shared library: %s (for architecture %s) "
"has no symbol table", old_dylib, arch_name);
else
fatal("old dynamic shared library: %s has no symbol table",
old_dylib);
}
if(old_ofile->mh != NULL){
old_symbols = (struct nlist *)
(old_ofile->object_addr + old_st->symoff);
old_symbols64 = NULL;
}
else{
old_symbols64 = (struct nlist_64 *)
(old_ofile->object_addr + old_st->symoff);
old_symbols = NULL;
}
old_tocs = (struct dylib_table_of_contents *)
(old_ofile->object_addr + old_dyst->tocoff);
old_strings = (char *)
(old_ofile->object_addr + old_st->stroff);
if(old_ofile->object_byte_sex != host_byte_sex){
if(old_ofile->mh != NULL)
swap_nlist(old_symbols, old_st->nsyms, host_byte_sex);
else
swap_nlist_64(old_symbols64, old_st->nsyms, host_byte_sex);
swap_dylib_table_of_contents(old_tocs, old_dyst->ntoc,
host_byte_sex);
}
for(i = 0; i < old_st->nsyms; i++){
if(old_ofile->mh != NULL)
n_strx = old_symbols[i].n_un.n_strx;
else
n_strx = old_symbols64[i].n_un.n_strx;
if(n_strx != 0 && n_strx > old_st->strsize){
if(arch_name != NULL)
fatal("malformed dynamic shared library: %s (for "
"architecture %s) (bad string table index for symbol "
"%u)", old_dylib, arch_name, i);
else
fatal("malformed dynamic shared library: %s (bad string "
"table index for symbol %u)", old_dylib, i);
}
}
for(i = 0; i < old_dyst->ntoc; i++){
if(old_tocs[i].symbol_index > old_st->nsyms){
if(arch_name != NULL)
fatal("malformed dynamic shared library: %s (for "
"architecture %s) (symbol_index field of table of "
"contents entry %u past the end of the symbol table)",
old_dylib, arch_name, i);
else
fatal("malformed dynamic shared library: %s (symbol_index "
"field of table of contents entry %u past the end of "
"the symbol table)", old_dylib, i);
}
}
new_st = NULL;
new_dyst = NULL;
lc = new_ofile->load_commands;
if(new_ofile->mh != NULL)
ncmds = new_ofile->mh->ncmds;
else
ncmds = new_ofile->mh64->ncmds;
for(i = 0; i < ncmds; i++){
if(new_st == NULL && lc->cmd == LC_SYMTAB){
new_st = (struct symtab_command *)lc;
}
else if(new_dyst == NULL && lc->cmd == LC_DYSYMTAB){
new_dyst = (struct dysymtab_command *)lc;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(new_st == NULL || new_st->nsyms == 0){
if(arch_name != NULL)
fatal("new dynamic shared library: %s (for architecture %s) "
"has no symbol table", new_dylib, arch_name);
else
fatal("new dynamic shared library: %s has no symbol table",
new_dylib);
}
if(new_ofile->mh != NULL){
new_symbols = (struct nlist *)
(new_ofile->object_addr + new_st->symoff);
new_symbols64 = NULL;
}
else{
new_symbols64 = (struct nlist_64 *)
(new_ofile->object_addr + new_st->symoff);
new_symbols = NULL;
}
new_strings = (char *)
(new_ofile->object_addr + new_st->stroff);
new_tocs = (struct dylib_table_of_contents *)
(new_ofile->object_addr + new_dyst->tocoff);
if(new_ofile->object_byte_sex != host_byte_sex){
if(new_ofile->mh != NULL)
swap_nlist(new_symbols, new_st->nsyms, host_byte_sex);
else
swap_nlist_64(new_symbols64, new_st->nsyms, host_byte_sex);
swap_dylib_table_of_contents(new_tocs, new_dyst->ntoc,
host_byte_sex);
}
for(i = 0; i < new_st->nsyms; i++){
if(new_ofile->mh != NULL)
n_strx = new_symbols[i].n_un.n_strx;
else
n_strx = new_symbols64[i].n_un.n_strx;
if(n_strx != 0 && n_strx > new_st->strsize){
if(arch_name != NULL)
fatal("malformed dynamic shared library: %s (for "
"architecture %s) (bad string table index for symbol "
"%u)", new_dylib, arch_name, i);
else
fatal("malformed dynamic shared library: %s (bad string "
"table index for symbol %u)", new_dylib, i);
}
}
for(i = 0; i < new_dyst->ntoc; i++){
if(new_tocs[i].symbol_index > new_st->nsyms){
if(arch_name != NULL)
fatal("malformed dynamic shared library: %s (for "
"architecture %s) (symbol_index field of table of "
"contents entry %u past the end of the symbol table)",
new_dylib, arch_name, i);
else
fatal("malformed dynamic shared library: %s (symbol_index "
"field of table of contents entry %u past the end of "
"the symbol table)", new_dylib, i);
}
}
missing_symbols = FALSE;
strings = new_strings;
if(new_dyst->ntoc != 0){
symbols = new_symbols;
symbols64 = new_symbols64;
}
else{
if(new_ofile->mh != NULL){
symbols = new_symbols + new_dyst->iextdefsym;
symbols64 = NULL;
}
else{
symbols64 = new_symbols64 + new_dyst->iextdefsym;
symbols = NULL;
}
}
for(i = 0; i < old_dyst->nextdefsym; i++){
if(new_ofile->mh != NULL)
n_strx = old_symbols[i + old_dyst->iextdefsym].n_un.n_strx;
else
n_strx = old_symbols64[i + old_dyst->iextdefsym].n_un.n_strx;
symbol_name = old_strings + n_strx;
if(new_dyst->ntoc != 0){
if(new_ofile->mh != NULL){
toc = bsearch(symbol_name, new_tocs, new_dyst->ntoc,
sizeof(struct dylib_table_of_contents),
(int (*)(const void *, const void *))
dylib_bsearch);
}
else{
toc = bsearch(symbol_name, new_tocs, new_dyst->ntoc,
sizeof(struct dylib_table_of_contents),
(int (*)(const void *, const void *))
dylib_bsearch64);
}
found = toc != NULL;
}
else{
if(new_ofile->mh != NULL){
sym = bsearch(symbol_name, symbols, new_dyst->nextdefsym,
sizeof(struct nlist),
(int (*)(const void *, const void *))
nlist_bsearch);
found = sym != NULL;
}
else{
sym64 = bsearch(symbol_name, symbols64,new_dyst->nextdefsym,
sizeof(struct nlist_64),
(int (*)(const void *, const void *))
nlist_bsearch64);
found = sym64 != NULL;
}
}
if(found == FALSE){
if(missing_symbols == FALSE){
if(arch_name != NULL)
printf("For architecture %s symbols defined in %s "
"not defined in %s:\n", arch_name, old_dylib,
new_dylib);
else
printf("symbols defined in %s not defined in %s:\n",
old_dylib, new_dylib);
missing_symbols = TRUE;
compatible = FALSE;
}
printf("%s\n", symbol_name);
}
}
if(new_api_allowed == TRUE)
return;
new_api = FALSE;
strings = old_strings;
if(old_dyst->ntoc != 0){
symbols = old_symbols;
symbols64 = old_symbols64;
}
else{
if(old_ofile->mh != NULL){
symbols = old_symbols + old_dyst->iextdefsym;
symbols64 = NULL;
}
else{
symbols64 = old_symbols64 + old_dyst->iextdefsym;
symbols = NULL;
}
}
for(i = 0; i < new_dyst->nextdefsym; i++){
if(new_ofile->mh != NULL)
n_strx = new_symbols[i + new_dyst->iextdefsym].n_un.n_strx;
else
n_strx = new_symbols64[i + new_dyst->iextdefsym].n_un.n_strx;
symbol_name = new_strings + n_strx;
if(old_dyst->ntoc != 0){
if(new_ofile->mh != NULL){
toc = bsearch(symbol_name, old_tocs, old_dyst->ntoc,
sizeof(struct dylib_table_of_contents),
(int (*)(const void *, const void *))
dylib_bsearch);
}
else{
toc = bsearch(symbol_name, old_tocs, old_dyst->ntoc,
sizeof(struct dylib_table_of_contents),
(int (*)(const void *, const void *))
dylib_bsearch64);
}
found = toc != NULL;
}
else{
if(new_ofile->mh != NULL){
sym = bsearch(symbol_name, symbols, old_dyst->nextdefsym,
sizeof(struct nlist),
(int (*)(const void *, const void *))
nlist_bsearch);
found = sym != NULL;
}
else{
sym64 = bsearch(symbol_name, symbols64,old_dyst->nextdefsym,
sizeof(struct nlist_64),
(int (*)(const void *, const void *))
nlist_bsearch64);
found = sym64 != NULL;
}
}
if(found == FALSE){
if(new_api == FALSE){
if(arch_name != NULL)
printf("For architecture %s compatibility versions are "
"the same but new symbols defined in %s not "
"defined in %s:\n",arch_name, new_dylib, old_dylib);
else
printf("compatibility versions are the same but new "
"symbols defined in %s not defined in %s:\n",
new_dylib, old_dylib);
new_api = TRUE;
compatible = FALSE;
}
printf("%s\n", symbol_name);
}
}
}
static
int
dylib_bsearch(
const char *symbol_name,
const struct dylib_table_of_contents *toc)
{
return(strcmp(symbol_name,
strings + symbols[toc->symbol_index].n_un.n_strx));
}
static
int
dylib_bsearch64(
const char *symbol_name,
const struct dylib_table_of_contents *toc)
{
return(strcmp(symbol_name,
strings + symbols64[toc->symbol_index].n_un.n_strx));
}
static
int
nlist_bsearch(
const char *symbol_name,
const struct nlist *sym)
{
return(strcmp(symbol_name, strings + sym->n_un.n_strx));
}
static
int
nlist_bsearch64(
const char *symbol_name,
const struct nlist_64 *sym64)
{
return(strcmp(symbol_name, strings + sym64->n_un.n_strx));
}