xref: /netbsd-src/usr.bin/crunch/crunchide/crunchide.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /* $NetBSD: crunchide.c,v 1.12 2004/08/24 12:25:26 wiz Exp $ */
2 
3 /*
4  * Copyright (c) 1997 Christopher G. Demetriou.  All rights reserved.
5  * Copyright (c) 1994 University of Maryland
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that
11  * copyright notice and this permission notice appear in supporting
12  * documentation, and that the name of U.M. not be used in advertising or
13  * publicity pertaining to distribution of the software without specific,
14  * written prior permission.  U.M. makes no representations about the
15  * suitability of this software for any purpose.  It is provided "as is"
16  * without express or implied warranty.
17  *
18  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
20  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
22  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
23  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  *
25  * Author: James da Silva, Systems Design and Analysis Group
26  *			   Computer Science Department
27  *			   University of Maryland at College Park
28  */
29 
30 /*
31  * crunchide.c - tiptoes through an a.out symbol table, hiding all defined
32  *	global symbols.  Allows the user to supply a "keep list" of symbols
33  *	that are not to be hidden.  This program relies on the use of the
34  * 	linker's -dc flag to actually put global bss data into the file's
35  * 	bss segment (rather than leaving it as undefined "common" data).
36  *
37  * 	The point of all this is to allow multiple programs to be linked
38  *	together without getting multiple-defined errors.
39  *
40  *	For example, consider a program "foo.c".  It can be linked with a
41  *	small stub routine, called "foostub.c", eg:
42  *	    int foo_main(int argc, char **argv){ return main(argc, argv); }
43  *      like so:
44  *	    cc -c foo.c foostub.c
45  *	    ld -dc -r foo.o foostub.o -o foo.combined.o
46  *	    crunchide -k _foo_main foo.combined.o
47  *	at this point, foo.combined.o can be linked with another program
48  * 	and invoked with "foo_main(argc, argv)".  foo's main() and any
49  * 	other globals are hidden and will not conflict with other symbols.
50  *
51  * TODO:
52  *	- resolve the theoretical hanging reloc problem (see check_reloc()
53  *	  below). I have yet to see this problem actually occur in any real
54  *	  program. In what cases will gcc/gas generate code that needs a
55  *	  relative reloc from a global symbol, other than PIC?  The
56  *	  solution is to not hide the symbol from the linker in this case,
57  *	  but to generate some random name for it so that it doesn't link
58  *	  with anything but holds the place for the reloc.
59  *      - arrange that all the BSS segments start at the same address, so
60  *	  that the final crunched binary BSS size is the max of all the
61  *	  component programs' BSS sizes, rather than their sum.
62  */
63 
64 #include <sys/cdefs.h>
65 #ifndef lint
66 __RCSID("$NetBSD: crunchide.c,v 1.12 2004/08/24 12:25:26 wiz Exp $");
67 #endif
68 
69 #include <unistd.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <fcntl.h>
74 #include <errno.h>
75 #include <a.out.h>
76 #include <sys/types.h>
77 #include <sys/stat.h>
78 
79 #include "extern.h"
80 
81 void usage(void);
82 
83 void add_to_keep_list(char *symbol);
84 void add_file_to_keep_list(char *filename);
85 
86 int hide_syms(const char *filename);
87 
88 int verbose;
89 
90 int
91 main(int argc, char *argv[])
92 {
93 	int ch, errors;
94 
95 	setprogname(argv[0]);
96 
97 	while ((ch = getopt(argc, argv, "k:f:v")) != -1)
98 		switch(ch) {
99 		case 'k':
100 			add_to_keep_list(optarg);
101 			break;
102 		case 'f':
103 			add_file_to_keep_list(optarg);
104 			break;
105 		case 'v':
106 			verbose = 1;
107 			break;
108 		default:
109 			usage();
110 		}
111 
112 	argc -= optind;
113 	argv += optind;
114 
115 	if (argc == 0)
116 		usage();
117 
118 	errors = 0;
119 	while (argc) {
120 		if (hide_syms(*argv))
121 			errors = 1;
122 		argc--, argv++;
123 	}
124 
125 	return errors;
126 }
127 
128 void
129 usage(void)
130 {
131 	fprintf(stderr,
132 		"Usage: %s [-k keep-symbol] [-f keep-list-file] object-file\n"
133 		"\t\t [object-file ...]\n",
134 		getprogname());
135 	exit(1);
136 }
137 
138 /* ---------------------------- */
139 
140 struct keep {
141 	struct keep *next;
142 	char *sym;
143 } *keep_list;
144 
145 void
146 add_to_keep_list(char *symbol)
147 {
148 	struct keep *newp, *prevp, *curp;
149 	int cmp;
150 
151 	cmp = 0;
152 
153 	for (curp = keep_list, prevp = NULL; curp; prevp = curp, curp = curp->next)
154 		if ((cmp = strcmp(symbol, curp->sym)) <= 0)
155 			break;
156 
157 	if (curp && cmp == 0)
158 		return;	/* already in table */
159 
160 	newp = (struct keep *) malloc(sizeof(struct keep));
161 	if (newp)
162 		newp->sym = strdup(symbol);
163 	if (newp == NULL || newp->sym == NULL) {
164 		fprintf(stderr, "%s: out of memory for keep list\n", getprogname());
165 		exit(1);
166 	}
167 
168 	newp->next = curp;
169 	if (prevp)
170 		prevp->next = newp;
171 	else
172 		keep_list = newp;
173 }
174 
175 int
176 in_keep_list(const char *symbol)
177 {
178 	struct keep *curp;
179 	int cmp;
180 
181 	cmp = 0;
182 
183 	for (curp = keep_list; curp; curp = curp->next)
184 		if((cmp = strcmp(symbol, curp->sym)) <= 0)
185 			break;
186 
187 	return curp && cmp == 0;
188 }
189 
190 void
191 add_file_to_keep_list(char *filename)
192 {
193 	FILE *keepf;
194 	char symbol[1024];
195 	int len;
196 
197 	if ((keepf = fopen(filename, "r")) == NULL) {
198 		perror(filename);
199 		usage();
200 	}
201 
202 	while (fgets(symbol, 1024, keepf)) {
203 		len = strlen(symbol);
204 		if (len && symbol[len-1] == '\n')
205 			symbol[len-1] = '\0';
206 
207 		add_to_keep_list(symbol);
208 	}
209 	fclose(keepf);
210 }
211 
212 /* ---------------------------- */
213 
214 struct {
215 	const char *name;
216 	int	(*check)(int, const char *);	/* 1 if match, zero if not */
217 	int	(*hide)(int, const char *);	/* non-zero if error */
218 } exec_formats[] = {
219 #ifdef NLIST_AOUT
220 	{	"a.out",	check_aout,	hide_aout,	},
221 #endif
222 #ifdef NLIST_COFF
223 	{	"COFF",		check_coff,	hide_coff,	},
224 #endif
225 #ifdef NLIST_ECOFF
226 	{	"ECOFF",	check_ecoff,	hide_ecoff,	},
227 #endif
228 #ifdef NLIST_ELF32
229 	{	"ELF32",	check_elf32,	hide_elf32,	},
230 #endif
231 #ifdef NLIST_ELF64
232 	{	"ELF64",	check_elf64,	hide_elf64,	},
233 #endif
234 };
235 
236 int
237 hide_syms(const char *filename)
238 {
239 	int fd, i, n, rv;
240 
241 	fd = open(filename, O_RDWR, 0);
242 	if (fd == -1) {
243 		perror(filename);
244 		return 1;
245 	}
246 
247 	rv = 0;
248 
249         n = sizeof exec_formats / sizeof exec_formats[0];
250         for (i = 0; i < n; i++) {
251 		if (lseek(fd, 0, SEEK_SET) != 0) {
252 			perror(filename);
253 			goto err;
254 		}
255                 if ((*exec_formats[i].check)(fd, filename) != 0)
256                         break;
257 	}
258 	if (i == n) {
259 		fprintf(stderr, "%s: unknown executable format\n", filename);
260 		goto err;
261 	}
262 
263 	if (verbose)
264 		fprintf(stderr, "%s is an %s binary\n", filename,
265 		    exec_formats[i].name);
266 
267 	if (lseek(fd, 0, SEEK_SET) != 0) {
268 		perror(filename);
269 		goto err;
270 	}
271 	rv = (*exec_formats[i].hide)(fd, filename);
272 
273 out:
274 	close(fd);
275 	return rv;
276 
277 err:
278 	rv = 1;
279 	goto out;
280 }
281