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