xref: /netbsd-src/sys/arch/amiga/stand/binpatch/binpatch.c (revision 9f2b1defb521b0c81854a912c748909480ce8149)
1 /*	$NetBSD: binpatch.c,v 1.15 2016/05/30 03:02:58 dholland Exp $	*/
2 
3 /* Author: Markus Wild mw@eunet.ch ???   */
4 /* Modified: Rob Leland leland@mitre.org */
5 
6 #include <sys/types.h>
7 #include <a.out.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 
14 #ifdef __NetBSD__
15 /*
16  * assume NMAGIC files are linked at 0 (for kernel)
17  */
18 #undef N_TXTADDR
19 #define N_TXTADDR(ex) \
20 	((N_GETMAGIC2(ex) == (ZMAGIC|0x10000) || N_GETMAGIC2(ex) == NMAGIC) ? \
21 	0 : AOUT_LDPGSZ)
22 #endif
23 
24 
25 static char synusage[] =
26 "NAME\n"
27 "\t%s - Allows the patching of BSD binaries\n"
28 "SYNOPSIS\n"
29 "\t%s [-HELP]\n"
30 "\t%s [-b|-w|-l] -s symbol[[[index]][=value]] binary\n"
31 "\t%s [-b|-w|-l] [-o offset] -s symbol [-r value] binary\n"
32 "\t%s [-b|-w|-l] [-o offset] -a address [-r value] binary\n";
33 
34 static char desusage[] =
35 "DESCRIPTION\n"
36 "\tAllows the patching of BSD binaries, for example, a distributed\n"
37 "\tkernel. Recient additions allows the user to index into an array\n"
38 "\tand assign a value. Binpatch has internal variables to allow\n"
39 "\tyou to test it on itself under NetBSD.\n"
40 "OPTIONS\n"
41 "\t-a  patch variable by specifying address in hex\n"
42 "\t-b  symbol or address to be patched is 1 byte\n"
43 "\t-l  symbol or address to be patched is 4 bytes  (default)\n"
44 "\t-o  offset to begin patching value relative to symbol or address\n"
45 "\t-r  replace value, and print out previous value to stdout\n"
46 "\t-s  patch variable by specifying symbol name. Use '[]'\n"
47 "\t    to specify the 'index'. If '-b, -w or -l' not specified\n"
48 "\t    then index value is used like an offset. Also can use '='\n"
49 "\t    to assign value\n"
50 "\t-w  symbol or address to be patched is 2 bytes\n"
51 "EXAMPLES\n"
52 "\tThis should print 100 (this is a nice reality check...)\n"
53 "\t\tbinpatch -l -s _hz netbsd\n"
54 "\tNow it gets more advanced, replace the value:\n"
55 "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd\n"
56 "\tNow patch a variable at a given 'index' not offset,\n"
57 "\tunder NetBSD you must use '', under AmigaDos CLI '' is optional.:\n"
58 "\t\tbinpatch -w -s '_vieww[4]' -r 0 a.out\n"
59 "\tsame as\n"
60 "\t\tbinpatch -w -o 8 -s _vieww -r 0 a.out\n"
61 "\tAnother example of using []\n"
62 "\t\tbinpatch -s '_viewl[4]' -r 0 a.out\n"
63 "\tsame as\n"
64 "\t\tbinpatch -o 4 -s _viewl -r 0 a.out\n"
65 "\tOne last example using '=' and []\n"
66 "\t\tbinpatch -w -s '_vieww[4]=2' a.out\n"
67 "\tSo if the kernel is not finding your drives, you could enable\n"
68 "\tall available debugging options, helping to shed light on that problem.\n"
69 "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd	scsi-level\n"
70 "\t\tbinpatch -l -s _sddebug -r 1 netbsd	sd-level (disk-driver)\n"
71 "\t\tbinpatch -l -s _acdebug -r 1 netbsd	autoconfig-level\n"
72 "SEE ALSO\n"
73 "\tbinpatch.c binpatch(1)\n";
74 
75 extern char *optarg;
76 extern int optind;
77 
78 void error(char *) __attribute__((__noreturn__));
79 static void Synopsis(char *program_name);
80 static void Usage(char *program_name);
81 static u_long FindAssign(char *symbol, u_long *rvalue);
82 static void FindOffset(char *symbol, u_long *index);
83 
84 /* The following variables are so binpatch can be tested on itself */
85 int test = 1;
86 int testbss;
87 char foo = 23;
88 char  viewb[10] = {0,0,1,0,1,1,0,1,1,1};
89 short vieww[10] = {0,0,1,0,1,1,0,1,1,1};
90 long  viewl[10] = {0,0,1,0,1,1,0,1,1,1};
91 /* End of test binpatch variables */
92 
93 int
main(int argc,char * argv[])94 main(int argc, char *argv[])
95 {
96 	struct exec e;
97 	int c;
98 	u_long addr = 0, offset = 0;
99 	/* Related to offset */
100 	u_long index = 0;
101 	u_long replace = 0, do_replace = 0;
102 	char *symbol = 0;
103 	/* default to long */
104 	char size = 4;
105 	/* Flag to say size option was set, used with index */
106 	char size_opt = 0;
107 	char *fname;
108 	/* Program name */
109 	char *pgname = argv[0];
110 	int fd;
111 	int type, off;
112 	u_long  lval;
113 	u_short sval;
114 	u_char  cval;
115 
116 
117 	while ((c = getopt(argc, argv, "H:a:bwlr:s:o:")) != -1) {
118 		switch (c) {
119 		case 'H':
120 			Usage(argv[0]);
121 			break;
122 		case 'a':
123 			if (addr || symbol) {
124 				error("only one address/symbol allowed");
125 			}
126 			if (!strncmp(optarg, "0x", 2)) {
127 				sscanf(optarg, "%x", &addr);
128 			} else {
129 				addr = atoi(optarg);
130 			}
131 			if (!addr) {
132 				error("invalid address");
133 			}
134 			break;
135 
136 		case 'b':
137 			size = 1;
138 			size_opt = 1;
139 			break;
140 
141 		case 'w':
142 			size = 2;
143 			size_opt = 1;
144 			break;
145 
146 		case 'l':
147 			size = 4;
148 			size_opt = 1;
149 			break;
150 
151 		case 'r':
152 			do_replace = 1;
153 			if (!strncmp(optarg, "0x", 2)) {
154 				sscanf(optarg, "%x", &replace);
155 			} else {
156 				replace = atoi(optarg);
157 			}
158 			break;
159 
160 		case 's':
161 			if (addr || symbol) {
162 				error("only one address/symbol allowed");
163 			}
164 			symbol = optarg;
165 			break;
166 
167 		case 'o':
168 			if (offset) {
169 				error("only one offset allowed");
170 			}
171 			if (!strncmp(optarg, "0x", 2)) {
172 				sscanf(optarg, "%x", &offset);
173 			} else {
174 				offset = atoi(optarg);
175 			}
176 			break;
177 		}
178 		/* end while switch() */
179 	}
180 
181 	if (argc > 1) {
182 		if (addr || symbol) {
183 			argv += optind;
184 			argc -= optind;
185 
186 			if (argc < 1) {
187 				error("No file to patch.");
188 			}
189 
190 			fname = argv[0];
191 			if ((fd = open(fname, 0)) < 0) {
192 				error("Can't open file");
193 			}
194 
195 			if (read(fd, &e, sizeof(e)) != sizeof(e)
196 			    || N_BADMAG(e)) {
197 				error("Not a valid executable.");
198 			}
199 
200 			/* fake mid, so the N_ macros work on the amiga.. */
201 			e.a_midmag |= 127 << 16;
202 
203 			if (symbol) {
204 				struct nlist nl[2];
205 
206 				if (offset == 0) {
207 					u_long new_do_replace = 0;
208 
209 					new_do_replace = FindAssign(symbol,
210 								&replace);
211 					if (new_do_replace && do_replace)
212 						error("Cannot use both '=' "
213 						      "and '-r' option!");
214 					FindOffset(symbol, &index);
215 					if (size_opt) {
216 						/* Treat like an index */
217 						offset = index*size;
218 					} else {
219 						/* Treat like an offset */
220 						offset = index;
221 					}
222 					if (new_do_replace)
223 						do_replace = new_do_replace;
224 				}
225 				nl[0].n_un.n_name = symbol;
226 				nl[1].n_un.n_name = 0;
227 				if (nlist(fname, nl) != 0) {
228 					fprintf(stderr, "Symbol is %s ",
229 						symbol);
230 					error("Symbol not found.");
231 				}
232 				addr = nl[0].n_value;
233 				type = nl[0].n_type & N_TYPE;
234 			} else {
235 				type = N_UNDF;
236 				if (addr >= N_TXTADDR(e) &&
237 				    addr < N_DATADDR(e)) {
238 					type = N_TEXT;
239 				} else if (addr >= N_DATADDR(e) &&
240 				    addr < N_DATADDR(e) + e.a_data) {
241 					type = N_DATA;
242 				}
243 			}
244 			addr += offset;
245 
246 			/*
247 			 * if replace-mode, have to reopen the file
248 			 * for writing. Can't do that from the
249 			 * beginning, or nlist() will not work (at
250 			 * least not under AmigaDOS)
251 			 */
252 			if (do_replace) {
253 				close(fd);
254 				if ((fd = open(fname, 2)) == -1) {
255 					error("Can't reopen file for writing.");
256 				}
257 			}
258 
259 			if (type != N_TEXT && type != N_DATA) {
260 				error("address/symbol is not in text "
261 				      "or data section.");
262 			}
263 
264 			if (type == N_TEXT) {
265 				off = addr - N_TXTADDR(e) + N_TXTOFF(e);
266 			} else {
267 				off = addr - N_DATADDR(e) + N_DATOFF(e);
268 			}
269 
270 			if (lseek(fd, off, 0) == -1) {
271 				error("lseek");
272 			}
273 
274 			/*
275 			 * not beautiful, but works on big and little
276 			 * endian machines
277 			 */
278 			switch (size) {
279 			case 1:
280 				if (read(fd, &cval, 1) != 1) {
281 					error("cread");
282 				}
283 				lval = cval;
284 				break;
285 
286 			case 2:
287 				if (read(fd, &sval, 2) != 2) {
288 					error("sread");
289 				}
290 				lval = sval;
291 				break;
292 
293 			case 4:
294 				if (read(fd, &lval, 4) != 4) {
295 					error("lread");
296 				}
297 				break;
298 			}/* switch size */
299 
300 
301 			if (symbol) {
302 				printf("%s(0x%x): %d (0x%x)\n", symbol, addr,
303 					lval, lval);
304 			} else {
305 				printf("0x%x: %d (0x%x)\n", addr, lval, lval);
306 			}
307 
308 			if (do_replace) {
309 				if (lseek(fd, off, 0) == -1) {
310 					error("write-lseek");
311 				}
312 				switch (size) {
313 				case 1:
314 					cval = replace;
315 					if (cval != replace) {
316 						error("byte-value overflow.");
317 					}
318 					if (write(fd, &cval, 1) != 1) {
319 						error("cwrite");
320 					}
321 					break;
322 
323 				case 2:
324 					sval = replace;
325 					if (sval != replace) {
326 						error("word-value overflow.");
327 					}
328 					if (write(fd, &sval, 2) != 2) {
329 						error("swrite");
330 					}
331 					break;
332 
333 				case 4:
334 					if (write(fd, &replace, 4) != 4) {
335 						error("lwrite");
336 					}
337 					break;
338 				}
339 				/* end switch(size) */
340 			}
341 			/* end if (do_replace) */
342 
343 			close(fd);
344 		} else {
345 			/* not (addr || symbol) */
346 			error("Must specify either address or symbol.");
347 		}
348 	} else {
349 		/* if argc <= 1 */
350 		Synopsis(pgname);
351 	}
352 
353 	return 0;
354 }
355 /* end main () */
356 
357 
358 
359 void
error(char * str)360 error(char *str)
361 {
362 	fprintf(stderr, "%s\n", str);
363 	exit(1);
364 }
365 
366 /* Give user very short help to avoid scrolling screen much */
367 static void
Synopsis(char * pgname)368 Synopsis(char *pgname)
369 {
370 	fprintf(stdout, synusage, pgname, pgname, pgname, pgname, pgname);
371 }
372 
373 
374 static void
Usage(char * pgname)375 Usage(char *pgname)
376 {
377 	Synopsis(pgname);
378 	fprintf(stdout, desusage);
379 	exit(0);
380 }
381 
382 
383 /*
384  * FindOffset() - Determine if there is an offset, -or- index
385  * embedded in the symbol.
386  *
387  * If there is, return it, and truncate symbol to exclude the [...].
388  *
389  * Example: If view is declared as short view[10],
390  *                and we want to index the 3rd. element.
391  *                which is offset = (3 -1)*sizeof(short) =4.
392  *          we would use view[4], which becomes view,4.
393  *
394  *          The way the code is implemented the [value] is
395  *          treated as a index if-and-only-if a '-b -w -l' option
396  *          was given. Otherwise it is treated like an offset.
397  *           See above documentation in for of help!
398  */
399 static void
FindOffset(char * symbol,u_long * index)400 FindOffset(char *symbol, u_long *index)
401 {
402 	/* Start of '[', now line must contain matching']' */
403 	char *sb = strchr(symbol, '[');
404 
405 	/* End of ']' */
406 	char *eb = strchr(symbol, ']');
407 
408 	/* symbol size */
409 	short sz = strlen(symbol);
410 
411 	if (sb) {
412 		if (eb && (eb > sb)) {
413 			if ((eb - symbol) == (sz - 1)) {
414 				/* Start of index */
415 				char *sindex;
416 				u_long newindex = 0;
417 
418 				/*
419 				 * In the future we could get fancy
420 				 * and parse the sindex string for
421 				 * mathmatical expressions like: (3 -
422 				 * 1)*2 = 4 from above example, ugh
423 				 * forget I mentioned ot :-) !
424 				 */
425 				sindex = sb + 1;
426 				*eb = '\0';
427 				newindex = (u_long)atoi(sindex);
428 				if (*index == 0) {
429 					*index = newindex;
430 					/* Make _view[3] look like _view */
431 					*sb = '\0';
432 				} else {
433 					fprintf(stderr, "Error index can "
434 						"only be specified once!\n");
435 				}
436 			} else {
437 				fprintf(stderr, "Error: Garbage "
438 					"trailing ']'\n");
439 			}
440 		} else {
441 			fprintf(stderr, "Error ']' in symbol before '[' !\n");
442 		}
443 	}
444 	/* end if sb != 0 */
445 }
446 /* end FindOffset */
447 
448 /*
449  * FindAssign : Scans symbol name for an '=number' strips it off of
450  * the symbol and proceeds.
451  */
452 static u_long
FindAssign(char * symbol,u_long * rvalue)453 FindAssign(char *symbol, u_long *rvalue)
454 {
455 	/* Assign symbol some number */
456 	char *ce = rindex(symbol, '=');
457 
458 	/* This should point at some number, no spaces allowed */
459 	char *cn = ce + 1;
460 
461 	/* flag for do_replace */
462 	u_long dr = 0;
463 
464 	if (ce) {
465 		/* number of variaables scanned in */
466 		int nscan;
467 
468 		/* get the number to assign to symbol and strip off = */
469 		for (cn=ce + 1; *cn==' '; cn++)
470 			;
471 		if (!strncmp(cn, "0x", 2)) {
472 			nscan = sscanf(cn, "%x", rvalue);
473 		} else {
474 			nscan = sscanf(cn, "%d", rvalue);
475 		}
476 		if (nscan != 1) {
477 			error("Invalid value following '='");
478 		}
479 		dr = 1;
480 		/* Now were left with just symbol */
481 		*ce = '\0';
482 	}
483 	/* end if (ce) */
484 	return(dr);
485 }
486 /* end FindAssign */
487