1/* 2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <limits.h> 27#include "stuff/ofile.h" 28#include "stuff/errors.h" 29#include "stuff/seg_addr_table.h" 30 31struct check_block { 32 char *install_name; 33 enum bool check_result; 34 struct seg_addr_table *entry; 35}; 36 37/* used by error routines as the name of the program */ 38char *progname = NULL; 39 40static void usage( 41 void); 42 43static void check_for_install_name( 44 struct ofile *ofile, 45 char *arch_name, 46 void *cookie); 47 48static void check_for_addresses( 49 struct ofile *ofile, 50 char *arch_name, 51 void *cookie); 52 53/* apple_version is created by the libstuff/Makefile */ 54extern char apple_version[]; 55char *version = apple_version; 56 57/* 58 * The check_dylib program. It takes a dynamic library file, an -install_name 59 * argument, a -seg_addr_table argument and a -seg_addr_table_filename argument. 60 * Then it preforms the following checks in the following order and if the 61 * specific check fails it returns a specific error code: 62 * 63 * Check: 64 * If the install_name of the dynamic library does not start with 65 * @executable_path checks the install_name of the dynamic library file 66 * against the specified -install_name argument and if it does not match it 67 * Returns: 2 68 * 69 * Check: 70 * Checks the specified -seg_addr_table for the -seg_addr_table_filename 71 * and if not found in the table it 72 * Returns: 3 73 * 74 * Check: 75 * Checks that the specified address in the -seg_addr_table for the 76 * -seg_addr_table_filename matches the dynamic library file. If not it 77 * Returns: 4 78 * 79 * Check: 80 * Checks that the specified address in the -seg_addr_table for the 81 * -seg_addr_table_filename and if it is zero then it 82 * Returns: 5 83 * 84 * If there is any other errors it returns 1 (EXIT_FAILURE). If all checks 85 * pass then it returns 0 (EXIT_SUCCESS). 86 */ 87int 88main( 89int argc, 90char **argv, 91char **envp) 92{ 93 int i; 94 uint32_t table_size; 95 char *install_name, *image_file_name, *seg_addr_table_name, 96 *seg_addr_table_filename; 97 struct check_block block; 98 struct seg_addr_table *seg_addr_table, *entry; 99 100 progname = argv[0]; 101 install_name = NULL; 102 image_file_name = NULL; 103 seg_addr_table = NULL; 104 seg_addr_table_filename = NULL; 105 106 for(i = 1; i < argc; i++){ 107 if(argv[i][0] == '-'){ 108 if(strcmp(argv[i], "-install_name") == 0){ 109 if(i + 1 == argc){ 110 error("missing argument(s) to %s option", argv[i]); 111 usage(); 112 } 113 if(install_name != NULL){ 114 error("more than one: %s option", argv[i]); 115 usage(); 116 } 117 install_name = argv[i+1]; 118 i++; 119 } 120 else if(strcmp(argv[i], "-seg_addr_table") == 0){ 121 if(i + 1 == argc){ 122 error("missing argument(s) to %s option", argv[i]); 123 usage(); 124 } 125 if(seg_addr_table != NULL){ 126 error("more than one: %s option", argv[i]); 127 usage(); 128 } 129 seg_addr_table_name = argv[i+1]; 130 seg_addr_table = parse_seg_addr_table(argv[i+1], 131 argv[i], argv[i+1], &table_size); 132 i++; 133 } 134 else if(strcmp(argv[i], "-seg_addr_table_filename") == 0){ 135 if(i + 1 == argc){ 136 error("missing argument(s) to %s option", argv[i]); 137 usage(); 138 } 139 if(seg_addr_table_filename != NULL){ 140 error("more than one: %s option", argv[i]); 141 usage(); 142 } 143 seg_addr_table_filename = argv[i+1]; 144 i++; 145 } 146 else{ 147 error("unknown option %s\n", argv[i]); 148 usage(); 149 } 150 } 151 else{ 152 if(image_file_name != NULL){ 153 error("more than file name specified (%s and %s)", 154 image_file_name, argv[i]); 155 usage(); 156 } 157 image_file_name = argv[i]; 158 } 159 } 160 if(image_file_name == NULL){ 161 error("must specify a file name to be checked"); 162 usage(); 163 } 164 if(install_name == NULL){ 165 error("must specify the -install_name <install_name> option"); 166 usage(); 167 } 168 if(seg_addr_table == NULL){ 169 error("must specify the -seg_addr_table <table_name> option"); 170 usage(); 171 } 172 if(seg_addr_table_filename == NULL){ 173 error("must specify the -seg_addr_table_filename <pathname> " "option"); 174 usage(); 175 } 176 177 /* 178 * The first check to perform is checking the install name to match 179 * the -install_name option. 180 */ 181 block.install_name = install_name; 182 block.check_result = TRUE; 183 ofile_process(image_file_name, NULL, 0, TRUE, 184 TRUE, TRUE, FALSE, check_for_install_name, &block); 185 if(block.check_result == FALSE) 186 return(2); 187 188 189 /* 190 * The next check to perform is to see if the -seg_addr_table_filename 191 * has an entry in the specified -seg_addr_table. 192 */ 193 entry = search_seg_addr_table(seg_addr_table, seg_addr_table_filename); 194 if(entry == NULL) 195 return(3); 196 197 /* 198 * The next check to perform is to see if the address in the 199 * -seg_addr_table entry matches the dynamic library file. 200 */ 201 block.entry = entry; 202 block.check_result = TRUE; 203 ofile_process(image_file_name, NULL, 0, TRUE, 204 TRUE, TRUE, FALSE, check_for_addresses, &block); 205 if(block.check_result == FALSE) 206 return(4); 207 208 /* 209 * The next check to perform is to see address in the -seg_addr_table 210 * for the -seg_addr_table_filename is zero. 211 */ 212 if((entry->split == FALSE && entry->seg1addr == 0) || 213 (entry->split == TRUE && (entry->segs_read_only_addr == 0 || 214 entry->segs_read_write_addr == 0)) ) 215 return(5); 216 217 return(EXIT_SUCCESS); 218} 219 220/* 221 * usage() prints the current usage message and exits indicating failure. 222 */ 223static 224void 225usage( 226void) 227{ 228 fprintf(stderr, "Usage: %s <file_name> -install_name <install_name> " 229 "-seg_addr_table <table_name> -seg_addr_table_filename " 230 "<path_name>\n", progname); 231 exit(EXIT_FAILURE); 232} 233 234static 235void 236check_for_install_name( 237struct ofile *ofile, 238char *arch_name, 239void *cookie) 240{ 241 uint32_t i; 242 struct check_block *block; 243 struct load_command *lc; 244 struct dylib_command *dl; 245 char *name; 246 247#ifdef DEBUG 248 printf("In check_for_install_name() ofile->file_name = %s", 249 ofile->file_name); 250 if(arch_name != NULL) 251 printf(" arch_name = %s\n", arch_name); 252 else 253 printf("\n"); 254#endif /* DEBUG */ 255 256 block = (struct check_block *)cookie; 257 if(ofile->mh == NULL){ 258 block->check_result = FALSE; 259 return; 260 } 261 262 lc = ofile->load_commands; 263 for(i = 0; i < ofile->mh->ncmds; i++){ 264 if(lc->cmd == LC_ID_DYLIB){ 265 dl = (struct dylib_command *)lc; 266 name = (char *)lc + dl->dylib.name.offset; 267 if(strncmp(name, "@executable_path", 268 sizeof("@executable_path") - 1) == 0) 269 return; 270 if(strcmp(name, block->install_name) != 0) 271 block->check_result = FALSE; 272 return; 273 } 274 lc = (struct load_command *)((char *)lc + lc->cmdsize); 275 } 276 block->check_result = FALSE; 277 return; 278} 279 280static 281void 282check_for_addresses( 283struct ofile *ofile, 284char *arch_name, 285void *cookie) 286{ 287 uint32_t i, segs_read_only_addr, segs_read_write_addr; 288 struct load_command *lc; 289 struct segment_command *sg, *first; 290 enum bool split; 291 struct check_block *block; 292 293#ifdef DEBUG 294 printf("In check_for_addresses() ofile->file_name = %s", 295 ofile->file_name); 296 if(arch_name != NULL) 297 printf(" arch_name = %s\n", arch_name); 298 else 299 printf("\n"); 300#endif /* DEBUG */ 301 302 block = (struct check_block *)cookie; 303 if(ofile->mh == NULL){ 304 block->check_result = FALSE; 305 return; 306 } 307 308 split = (ofile->mh->flags & MH_SPLIT_SEGS) == MH_SPLIT_SEGS; 309 if(block->entry->split != split){ 310 block->check_result = FALSE; 311 return; 312 } 313 lc = ofile->load_commands; 314 first = NULL; 315 segs_read_only_addr = UINT_MAX; 316 segs_read_write_addr = UINT_MAX; 317 for(i = 0; i < ofile->mh->ncmds; i++){ 318 if(lc->cmd == LC_SEGMENT){ 319 sg = (struct segment_command *)lc; 320 if(first == NULL){ 321 first = sg; 322 if(split == FALSE && 323 first->vmaddr != block->entry->seg1addr){ 324 block->check_result = FALSE; 325 return; 326 } 327 } 328 if((sg->initprot & VM_PROT_WRITE) == 0){ 329 if(split == TRUE && sg->vmaddr < segs_read_only_addr) 330 segs_read_only_addr = sg->vmaddr; 331 } 332 else{ 333 if(split == TRUE && sg->vmaddr < segs_read_write_addr) 334 segs_read_write_addr = sg->vmaddr; 335 } 336 } 337 lc = (struct load_command *)((char *)lc + lc->cmdsize); 338 } 339 if(split == TRUE){ 340 if(segs_read_only_addr != block->entry->segs_read_only_addr || 341 segs_read_write_addr != block->entry->segs_read_write_addr){ 342 block->check_result = FALSE; 343 return; 344 } 345 } 346} 347