xref: /netbsd/sys/arch/amiga/stand/binpatch/binpatch.c (revision c4a72b64)
1 /*	$NetBSD: binpatch.c,v 1.9 2002/05/31 20:52:14 thorpej 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 : __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 static char desusage[] =
34 "DESCRIPTION\n"
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"
39 "OPTIONS\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"
50 "EXAMPLES\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"
58 "\tsame as\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"
62 "\tsame as\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"
71 "SEE ALSO\n"
72 "\tbinpatch.c binpatch(1)\n";
73 
74 extern char *optarg;
75 extern int optind;
76 
77 volatile void error (char *);
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);
82 
83 /* The following variables are so binpatch can be tested on itself */
84 int test = 1;
85 int testbss;
86 char foo = 23;
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 */
91 int
92 main(int argc, char *argv[])
93 {
94   struct exec e;
95   int c;
96   u_long addr = 0, offset = 0;
97   u_long index = 0;/* Related to offset */
98   u_long replace = 0, do_replace = 0;
99   char *symbol = 0;
100   char size = 4;  /* default to long */
101   char size_opt = 0; /* Flag to say size option was set, used with index */
102   char *fname;
103   char *pgname = argv[0]; /* Program name */
104   int fd;
105   int type, off;
106   u_long  lval;
107   u_short sval;
108   u_char  cval;
109 
110 
111   while ((c = getopt (argc, argv, "H:a:bwlr:s:o:")) != -1)
112     switch (c)
113       {
114       case 'H':
115         Usage(argv[0]);
116         break;
117       case 'a':
118 	if (addr || symbol)
119 	  error ("only one address/symbol allowed");
120 	if (! strncmp (optarg, "0x", 2))
121 	  sscanf (optarg, "%x", &addr);
122 	else
123 	  addr = atoi (optarg);
124 	if (! addr)
125 	  error ("invalid address");
126 	break;
127 
128       case 'b':
129 	size = 1;
130         size_opt = 1;
131 	break;
132 
133       case 'w':
134 	size = 2;
135         size_opt = 1;
136 	break;
137 
138       case 'l':
139 	size = 4;
140         size_opt = 1;
141 	break;
142 
143       case 'r':
144 	do_replace = 1;
145 	if (! strncmp (optarg, "0x", 2))
146 	  sscanf (optarg, "%x", &replace);
147 	else
148 	  replace = atoi (optarg);
149 	break;
150 
151       case 's':
152 	if (addr || symbol)
153 	  error ("only one address/symbol allowed");
154 	symbol = optarg;
155 	break;
156 
157       case 'o':
158 	if (offset)
159 	  error ("only one offset allowed");
160 	if (! strncmp (optarg, "0x", 2))
161 	  sscanf (optarg, "%x", &offset);
162 	else
163           offset = atoi (optarg);
164         break;
165       }/* while switch() */
166 
167   if (argc > 1)
168   {
169     if (addr || symbol)
170     {
171       argv += optind;
172       argc -= optind;
173 
174       if (argc < 1)
175         error ("No file to patch.");
176 
177       fname = argv[0];
178       if ((fd = open (fname, 0)) < 0)
179         error ("Can't open file");
180 
181       if (read (fd, &e, sizeof (e)) != sizeof (e)
182         || N_BADMAG (e))
183         error ("Not a valid executable.");
184 
185       /* fake mid, so the N_ macros work on the amiga.. */
186       e.a_midmag |= 127 << 16;
187 
188       if (symbol)
189       {
190         struct nlist nl[2];
191         if (offset == 0)
192 	{
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);
198             if (size_opt)
199                offset = index*size; /* Treat like an index */
200             else
201                offset = index; /* Treat index like an offset */
202 	    if (new_do_replace)
203 	       do_replace = new_do_replace;
204 	}
205         nl[0].n_un.n_name = symbol;
206         nl[1].n_un.n_name = 0;
207         if (nlist (fname, nl) != 0)
208 	{
209           fprintf(stderr,"Symbol is %s ",symbol);
210 	  error ("Symbol not found.");
211         }
212         addr = nl[0].n_value;
213         type = nl[0].n_type & N_TYPE;
214       }
215       else
216       {
217         type = N_UNDF;
218         if (addr >= N_TXTADDR(e) && addr < N_DATADDR(e))
219 	  type = N_TEXT;
220         else if (addr >= N_DATADDR(e) && addr < N_DATADDR(e) + e.a_data)
221 	  type = N_DATA;
222       }
223       addr += offset;
224 
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) */
228       if (do_replace)
229       {
230         close (fd);
231         if ((fd = open (fname, 2)) == -1)
232 	  error ("Can't reopen file for writing.");
233       }
234 
235       if (type != N_TEXT && type != N_DATA)
236         error ("address/symbol is not in text or data section.");
237 
238       if (type == N_TEXT)
239         off = addr - N_TXTADDR(e) + N_TXTOFF(e);
240       else
241         off = addr - N_DATADDR(e) + N_DATOFF(e);
242 
243       if (lseek (fd, off, 0) == -1)
244         error ("lseek");
245 
246       /* not beautiful, but works on big and little endian machines */
247       switch (size)
248         {
249         case 1:
250           if (read (fd, &cval, 1) != 1)
251 	    error ("cread");
252           lval = cval;
253           break;
254 
255         case 2:
256           if (read (fd, &sval, 2) != 2)
257 	    error ("sread");
258           lval = sval;
259           break;
260 
261         case 4:
262           if (read (fd, &lval, 4) != 4)
263 	    error ("lread");
264           break;
265         }/* switch size */
266 
267 
268       if (symbol)
269         printf ("%s(0x%x): %d (0x%x)\n", symbol, addr, lval, lval);
270       else
271         printf ("0x%x: %d (0x%x)\n", addr, lval, lval);
272 
273       if (do_replace)
274       {
275         if (lseek (fd, off, 0) == -1)
276 	  error ("write-lseek");
277         switch (size)
278 	  {
279 	  case 1:
280 	    cval = replace;
281 	    if (cval != replace)
282 	      error ("byte-value overflow.");
283 	    if (write (fd, &cval, 1) != 1)
284 	      error ("cwrite");
285 	    break;
286 
287 	  case 2:
288 	    sval = replace;
289 	    if (sval != replace)
290 	      error ("word-value overflow.");
291 	    if (write (fd, &sval, 2) != 2)
292 	      error ("swrite");
293 	    break;
294 
295 	  case 4:
296 	    if (write (fd, &replace, 4) != 4)
297 	      error ("lwrite");
298 	    break;
299 	  }/* switch(size) */
300       }/* if (do_replace) */
301 
302       close (fd);
303     }/* if(addr || symbol ) */
304     else
305     {
306       error("Must specify either address or symbol.");
307     }
308   }/* if argc < 1 */
309   else
310   {
311     Synopsis(pgname);
312   }
313   return(0);
314 }/* main () */
315 
316 
317 
318 volatile void error (char *str)
319 {
320   fprintf (stderr, "%s\n", str);
321   exit (1);
322 }
323 
324 /* Give user very short help to avoid scrolling screen much */
325 static void Synopsis(char *pgname)
326 {
327   fprintf(stdout,synusage,pgname,pgname,pgname,pgname,pgname);
328 }
329 
330 
331 static void Usage(char *pgname)
332 {
333   Synopsis(pgname);
334   fprintf(stdout,desusage);
335   exit(0);
336 }
337 
338 
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
342                  exclude the [...].
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!
351 */
352 static void FindOffset(char *symbol,u_long *index)
353 {
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 */
358   if (sb)
359   {
360     if (eb && (eb > sb))
361     {
362       if ((eb - symbol) == (sz - 1))
363       {
364         char *sindex; /* Start of index */
365         u_long newindex = 0;
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 :-) !
370         */
371         sindex = sb + 1;
372         *eb = '\0';
373         newindex = (u_long)atoi(sindex);
374         if (*index == 0)
375         {
376           *index = newindex;
377           *sb = '\0'; /* Make _view[3] look like _view */
378         }
379         else
380           fprintf(stderr,"Error index can only be specified once!\n");
381       }
382       else
383       {
384         fprintf(stderr,"Error: Garbage trailing ']'\n");
385       }
386     }
387     else
388     {
389        fprintf(stderr,"Error ']' in symbol before '[' !\n");
390     }
391   }/* if sb != 0 */
392 }/* FindOffset */
393 
394 /* FindAssign : Scans symbol name for an '=number' strips it off
395    of the symbol and proceeds.
396 */
397 static u_long FindAssign(char *symbol,u_long *rvalue)
398 {
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 */
402   if (ce)
403   {
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++)
407     ;
408     if (! strncmp (cn, "0x", 2))
409 	nscan = sscanf (cn, "%x",rvalue);
410     else
411         nscan = sscanf(cn,"%d",rvalue);
412     if (nscan != 1)
413       error("Invalid value following '='");
414     dr = 1;
415     *ce = '\0';/* Now were left with just symbol */
416   }/* if (ce) */
417   return(dr);
418 }/* FindAssign */
419