1 /* $NetBSD: binpatch.c,v 1.10 2002/12/10 17:14:05 thorpej Exp $ */
3 /* Author: Markus Wild mw@eunet.ch ??? */
4 /* Modified: Rob Leland leland@mitre.org */
16 * assume NMAGIC files are linked at 0 (for kernel)
19 #define N_TXTADDR(ex) \
20 ((N_GETMAGIC2(ex) == (ZMAGIC|0x10000) || N_GETMAGIC2(ex) == NMAGIC) ? \
25 static char synusage
[] =
27 "\t%s - Allows the patching of BSD binaries\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 static char desusage
[] =
35 "\tAllows the patching of BSD binaries, for example,a distributed\n"
36 "\tkernel. Recient additions allows the user to index into an array\n"
37 "\tand assign a value. Binpatch has internal variables to allow\n"
38 "\tyou to test it on itself under NetBSD.\n"
40 "\t-a patch variable by specifying address in hex\n"
41 "\t-b symbol or address to be patched is 1 byte\n"
42 "\t-l symbol or address to be patched is 4 bytes (default)\n"
43 "\t-o offset to begin patching value relative to symbol or address\n"
44 "\t-r replace value, and print out previous value to stdout\n"
45 "\t-s patch variable by specifying symbol name. Use '[]'\n"
46 "\t to specify the 'index'. If '-b, -w or -l' not specified\n"
47 "\t then index value is used like an offset. Also can use '='\n"
48 "\t to assign value\n"
49 "\t-w symbol or address to be patched is 2 bytes\n"
51 "\tThis should print 100 (this is a nice reality check...)\n"
52 "\t\tbinpatch -l -s _hz netbsd\n"
53 "\tNow it gets more advanced, replace the value:\n"
54 "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd\n"
55 "\tNow patch a variable at a given 'index' not offset,\n"
56 "\tunder NetBSD you must use '', under AmigaDos CLI '' is optional.:\n"
57 "\t\tbinpatch -w -s '_vieww[4]' -r 0 a.out\n"
59 "\t\tbinpatch -w -o 8 -s _vieww -r 0 a.out\n"
60 "\tAnother example of using []\n"
61 "\t\tbinpatch -s '_viewl[4]' -r 0 a.out\n"
63 "\t\tbinpatch -o 4 -s _viewl -r 0 a.out\n"
64 "\tOne last example using '=' and []\n"
65 "\t\tbinpatch -w -s '_vieww[4]=2' a.out\n"
66 "\tSo if the kernel is not finding your drives, you could enable\n"
67 "\tall available debugging options, helping to shed light on that problem.\n"
68 "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd scsi-level\n"
69 "\t\tbinpatch -l -s _sddebug -r 1 netbsd sd-level (disk-driver)\n"
70 "\t\tbinpatch -l -s _acdebug -r 1 netbsd autoconfig-level\n"
72 "\tbinpatch.c binpatch(1)\n";
77 void error (char *) __attribute__((__noreturn__
));
78 static void Synopsis(char *program_name
);
79 static void Usage(char *program_name
);
80 static u_long
FindAssign(char *symbol
,u_long
*rvalue
);
81 static void FindOffset(char *symbol
,u_long
*index
);
83 /* The following variables are so binpatch can be tested on itself */
87 char viewb
[10] = {0,0,1,0,1,1,0,1,1,1};
88 short vieww
[10] = {0,0,1,0,1,1,0,1,1,1};
89 long viewl
[10] = {0,0,1,0,1,1,0,1,1,1};
90 /* End of test binpatch variables */
92 main(int argc
, char *argv
[])
96 u_long addr
= 0, offset
= 0;
97 u_long index
= 0;/* Related to offset */
98 u_long replace
= 0, do_replace
= 0;
100 char size
= 4; /* default to long */
101 char size_opt
= 0; /* Flag to say size option was set, used with index */
103 char *pgname
= argv
[0]; /* Program name */
111 while ((c
= getopt (argc
, argv
, "H:a:bwlr:s:o:")) != -1)
119 error ("only one address/symbol allowed");
120 if (! strncmp (optarg
, "0x", 2))
121 sscanf (optarg
, "%x", &addr
);
123 addr
= atoi (optarg
);
125 error ("invalid address");
145 if (! strncmp (optarg
, "0x", 2))
146 sscanf (optarg
, "%x", &replace
);
148 replace
= atoi (optarg
);
153 error ("only one address/symbol allowed");
159 error ("only one offset allowed");
160 if (! strncmp (optarg
, "0x", 2))
161 sscanf (optarg
, "%x", &offset
);
163 offset
= atoi (optarg
);
165 }/* while switch() */
175 error ("No file to patch.");
178 if ((fd
= open (fname
, 0)) < 0)
179 error ("Can't open file");
181 if (read (fd
, &e
, sizeof (e
)) != sizeof (e
)
183 error ("Not a valid executable.");
185 /* fake mid, so the N_ macros work on the amiga.. */
186 e
.a_midmag
|= 127 << 16;
193 u_long new_do_replace
= 0;
194 new_do_replace
= FindAssign(symbol
,&replace
);
195 if (new_do_replace
&& do_replace
)
196 error("Cannot use both '=' and '-r' option!");
197 FindOffset(symbol
,&index
);
199 offset
= index
*size
; /* Treat like an index */
201 offset
= index
; /* Treat index like an offset */
203 do_replace
= new_do_replace
;
205 nl
[0].n_un
.n_name
= symbol
;
206 nl
[1].n_un
.n_name
= 0;
207 if (nlist (fname
, nl
) != 0)
209 fprintf(stderr
,"Symbol is %s ",symbol
);
210 error ("Symbol not found.");
212 addr
= nl
[0].n_value
;
213 type
= nl
[0].n_type
& N_TYPE
;
218 if (addr
>= N_TXTADDR(e
) && addr
< N_DATADDR(e
))
220 else if (addr
>= N_DATADDR(e
) && addr
< N_DATADDR(e
) + e
.a_data
)
225 /* if replace-mode, have to reopen the file for writing.
226 Can't do that from the beginning, or nlist() will not
227 work (at least not under AmigaDOS) */
231 if ((fd
= open (fname
, 2)) == -1)
232 error ("Can't reopen file for writing.");
235 if (type
!= N_TEXT
&& type
!= N_DATA
)
236 error ("address/symbol is not in text or data section.");
239 off
= addr
- N_TXTADDR(e
) + N_TXTOFF(e
);
241 off
= addr
- N_DATADDR(e
) + N_DATOFF(e
);
243 if (lseek (fd
, off
, 0) == -1)
246 /* not beautiful, but works on big and little endian machines */
250 if (read (fd
, &cval
, 1) != 1)
256 if (read (fd
, &sval
, 2) != 2)
262 if (read (fd
, &lval
, 4) != 4)
269 printf ("%s(0x%x): %d (0x%x)\n", symbol
, addr
, lval
, lval
);
271 printf ("0x%x: %d (0x%x)\n", addr
, lval
, lval
);
275 if (lseek (fd
, off
, 0) == -1)
276 error ("write-lseek");
282 error ("byte-value overflow.");
283 if (write (fd
, &cval
, 1) != 1)
290 error ("word-value overflow.");
291 if (write (fd
, &sval
, 2) != 2)
296 if (write (fd
, &replace
, 4) != 4)
300 }/* if (do_replace) */
303 }/* if(addr || symbol ) */
306 error("Must specify either address or symbol.");
318 void error (char *str
)
320 fprintf (stderr
, "%s\n", str
);
324 /* Give user very short help to avoid scrolling screen much */
325 static void Synopsis(char *pgname
)
327 fprintf(stdout
,synusage
,pgname
,pgname
,pgname
,pgname
,pgname
);
331 static void Usage(char *pgname
)
334 fprintf(stdout
,desusage
);
339 /* FindOffset() - Determine if there is an offset, -or- index
340 embedded in the symbol.
341 If there is, return it, and truncate symbol to
343 Example: If view is declared as short view[10],
344 and we want to index the 3rd. element.
345 which is offset = (3 -1)*sizeof(short) =4.
346 we would use view[4], which becomes view,4.
347 The was the code is implemented the [value] is
348 treated as a index if-and-only-if a '-b -w -l' option
349 was given. Otherwise it is treated like an offset.
350 See above documentation in for of help!
352 static void FindOffset(char *symbol
,u_long
*index
)
354 char *sb
=strchr(symbol
,'['); /* Start of '[', now line must
355 contain matching']' */
356 char *eb
=strchr(symbol
,']'); /* End of ']' */
357 short sz
=strlen(symbol
); /* symbol size */
362 if ((eb
- symbol
) == (sz
- 1))
364 char *sindex
; /* Start of index */
366 /* In the future we could get fancy and parse the
367 sindex string for mathmatical expressions like:
368 (3 - 1)*2 = 4 from above example,
369 ugh forget I mentioned ot :-) !
373 newindex
= (u_long
)atoi(sindex
);
377 *sb
= '\0'; /* Make _view[3] look like _view */
380 fprintf(stderr
,"Error index can only be specified once!\n");
384 fprintf(stderr
,"Error: Garbage trailing ']'\n");
389 fprintf(stderr
,"Error ']' in symbol before '[' !\n");
394 /* FindAssign : Scans symbol name for an '=number' strips it off
395 of the symbol and proceeds.
397 static u_long
FindAssign(char *symbol
,u_long
*rvalue
)
399 char *ce
= rindex(symbol
,'='); /* Assign symbol some number */
400 char *cn
= ce
+ 1; /* This should point at some number, no spaces allowed */
401 u_long dr
= 0; /* flag for do_replace */
404 int nscan
; /* number of variaables scanned in */
405 /* get the number to assign to symbol and strip off = */
406 for (cn
=ce
+ 1;((*cn
==' ')&&(*cn
!='\0'));cn
++)
408 if (! strncmp (cn
, "0x", 2))
409 nscan
= sscanf (cn
, "%x",rvalue
);
411 nscan
= sscanf(cn
,"%d",rvalue
);
413 error("Invalid value following '='");
415 *ce
= '\0';/* Now were left with just symbol */