cctools-port/cctools/misc/cmpdylib.c

713 lines
20 KiB
C

/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* 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 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <stdio.h>
#include <stdlib.h>
#include "stuff/ofile.h"
#include "stuff/errors.h"
#include "stuff/bytesex.h"
#include "stuff/allocate.h"
/* name of the program for error messages (argv[0]) */
__private_extern__ char *progname = NULL;
/* The filenames of the old and new dylib */
static char *old_dylib = NULL;
static char *new_dylib = NULL;
/*
* This is a flag use by process_old() and compare() to make sure each
* architecture in the old library is in the new library.
*/
static enum bool arch_processed = FALSE;
static char *arch_name_being_processed = NULL;
/* The result of the dylib comparison */
static enum bool compatible = TRUE;
/* the byte sex of the machine this program is running on */
static enum byte_sex host_byte_sex = UNKNOWN_BYTE_SEX;
/*
* These are pointers to strings and symbols used to search of the table of
* contents of a library. These have to be static and not local so that
* check_global_symbols() can set them and that dylib_bsearch() can use them.
*/
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);
/* apple_version is created by the libstuff/Makefile */
extern char apple_version[];
char *version = apple_version;
/*
* The program cmpdylib. This compares an old and an new dynamic shared library
* for compatiblity. Usage:
*
* cmpdylib old_dylib new_dylib
*
* It exits non-zero for incompatible libraries and prints why the libraries are
* incompatible. It exit zero and prints nothing for compatible libraries.
*/
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);
}
/*
* process_old() is called once for each architecture in the old dynamic shared
* library and then causes compare() to be called for the same architecture
* in the new dynamic shared library.
*/
static
void
process_old(
struct ofile *old_ofile,
char *arch_name,
void *cookie)
{
struct arch_flag arch_flag;
/* check to make sure this is a dynamic shared library */
check_dylib(old_ofile, arch_name);
/* fill in the architecure info */
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);
}
/*
* compare() checks the new dynamic shared library against the old one for the
* same architecture. The old dynamic shared library's ofile struct is passed
* as the cookie.
*/
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 to make sure this is a dynamic shared library */
check_dylib(new_ofile, arch_name);
old_ofile = (struct ofile *)cookie;
/* Get the LC_ID_DYLIB from the old dylib */
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);
}
/* Get the LC_ID_DYLIB from the new 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);
}
/* check the values of the LC_ID_DYLIB's */
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;
}
/*
* check_dylib() checks to make sure this is a dynamic shared library. If not
* it prints an error and exits.
*/
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);
}
/*
* check_global_symbols() checks to see if all the global symbols defined in the
* old library are defined in the new library. If not it prints the ones not
* defined and sets compatible to FALSE. If new_api_allowed is FALSE then it
* checks to see if there are any global symbols that are in the new library
* that are not in the old library. If so it prints those and sets compatible
* to FALSE.
*/
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;
/*
* Pickup the symbolic info for the old dylib.
*/
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);
}
}
/*
* Pickup the symbolic info for the new dylib.
*/
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);
}
}
/*
* Now check to see if all the global symbols defined in the old library
* are defined in the new library. Prints the ones that are not and
* sets compatible to FALSE.
*/
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 is allowed then checking of global symbols is done.
*/
if(new_api_allowed == TRUE)
return;
/*
* New api is not allowed so check to make sure no symbols in the new
* library are not in the old library.
*/
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);
}
}
}
/*
* Function for bsearch() for finding a symbol name in a dylib table of
* contents.
*/
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));
}
/*
* Function for bsearch() for finding a symbol name in the sorted list of
* defined external symbols.
*/
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));
}