xref: /netbsd/sys/arch/amiga/stand/binpatch/binpatch.c (revision bf9ec67e)
1 /*	$NetBSD: binpatch.c,v 1.8 2002/01/26 13:16:06 aymeric 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
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