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