/* $NetBSD: binpatch.c,v 1.15 2016/05/30 03:02:58 dholland Exp $ */
/* Author: Markus Wild
[email protected] ??? */
/* Modified: Rob Leland
[email protected] */
#include <sys/types.h>
#include <a.out.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef __NetBSD__
/*
* assume NMAGIC files are linked at 0 (for kernel)
*/
#undef N_TXTADDR
#define N_TXTADDR(ex) \
((N_GETMAGIC2(ex) == (ZMAGIC|0x10000) || N_GETMAGIC2(ex) == NMAGIC) ? \
0 : AOUT_LDPGSZ)
#endif
static char synusage[] =
"NAME\n"
"\t%s - Allows the patching of BSD binaries\n"
"SYNOPSIS\n"
"\t%s [-HELP]\n"
"\t%s [-b|-w|-l] -s symbol[[[index]][=value]] binary\n"
"\t%s [-b|-w|-l] [-o offset] -s symbol [-r value] binary\n"
"\t%s [-b|-w|-l] [-o offset] -a address [-r value] binary\n";
static char desusage[] =
"DESCRIPTION\n"
"\tAllows the patching of BSD binaries, for example, a distributed\n"
"\tkernel. Recient additions allows the user to index into an array\n"
"\tand assign a value. Binpatch has internal variables to allow\n"
"\tyou to test it on itself under NetBSD.\n"
"OPTIONS\n"
"\t-a patch variable by specifying address in hex\n"
"\t-b symbol or address to be patched is 1 byte\n"
"\t-l symbol or address to be patched is 4 bytes (default)\n"
"\t-o offset to begin patching value relative to symbol or address\n"
"\t-r replace value, and print out previous value to stdout\n"
"\t-s patch variable by specifying symbol name. Use '[]'\n"
"\t to specify the 'index'. If '-b, -w or -l' not specified\n"
"\t then index value is used like an offset. Also can use '='\n"
"\t to assign value\n"
"\t-w symbol or address to be patched is 2 bytes\n"
"EXAMPLES\n"
"\tThis should print 100 (this is a nice reality check...)\n"
"\t\tbinpatch -l -s _hz netbsd\n"
"\tNow it gets more advanced, replace the value:\n"
"\t\tbinpatch -l -s _sbic_debug -r 1 netbsd\n"
"\tNow patch a variable at a given 'index' not offset,\n"
"\tunder NetBSD you must use '', under AmigaDos CLI '' is optional.:\n"
"\t\tbinpatch -w -s '_vieww[4]' -r 0 a.out\n"
"\tsame as\n"
"\t\tbinpatch -w -o 8 -s _vieww -r 0 a.out\n"
"\tAnother example of using []\n"
"\t\tbinpatch -s '_viewl[4]' -r 0 a.out\n"
"\tsame as\n"
"\t\tbinpatch -o 4 -s _viewl -r 0 a.out\n"
"\tOne last example using '=' and []\n"
"\t\tbinpatch -w -s '_vieww[4]=2' a.out\n"
"\tSo if the kernel is not finding your drives, you could enable\n"
"\tall available debugging options, helping to shed light on that problem.\n"
"\t\tbinpatch -l -s _sbic_debug -r 1 netbsd scsi-level\n"
"\t\tbinpatch -l -s _sddebug -r 1 netbsd sd-level (disk-driver)\n"
"\t\tbinpatch -l -s _acdebug -r 1 netbsd autoconfig-level\n"
"SEE ALSO\n"
"\tbinpatch.c binpatch(1)\n";
extern char *optarg;
extern int optind;
void error(char *) __attribute__((__noreturn__));
static void Synopsis(char *program_name);
static void Usage(char *program_name);
static u_long FindAssign(char *symbol, u_long *rvalue);
static void FindOffset(char *symbol, u_long *index);
/* The following variables are so binpatch can be tested on itself */
int test = 1;
int testbss;
char foo = 23;
char viewb[10] = {0,0,1,0,1,1,0,1,1,1};
short vieww[10] = {0,0,1,0,1,1,0,1,1,1};
long viewl[10] = {0,0,1,0,1,1,0,1,1,1};
/* End of test binpatch variables */
int
main(int argc, char *argv[])
{
struct exec e;
int c;
u_long addr = 0, offset = 0;
/* Related to offset */
u_long index = 0;
u_long replace = 0, do_replace = 0;
char *symbol = 0;
/* default to long */
char size = 4;
/* Flag to say size option was set, used with index */
char size_opt = 0;
char *fname;
/* Program name */
char *pgname = argv[0];
int fd;
int type, off;
u_long lval;
u_short sval;
u_char cval;
while ((c = getopt(argc, argv, "H:a:bwlr:s:o:")) != -1) {
switch (c) {
case 'H':
Usage(argv[0]);
break;
case 'a':
if (addr || symbol) {
error("only one address/symbol allowed");
}
if (!strncmp(optarg, "0x", 2)) {
sscanf(optarg, "%x", &addr);
} else {
addr = atoi(optarg);
}
if (!addr) {
error("invalid address");
}
break;
case 'b':
size = 1;
size_opt = 1;
break;
case 'w':
size = 2;
size_opt = 1;
break;
case 'l':
size = 4;
size_opt = 1;
break;
case 'r':
do_replace = 1;
if (!strncmp(optarg, "0x", 2)) {
sscanf(optarg, "%x", &replace);
} else {
replace = atoi(optarg);
}
break;
case 's':
if (addr || symbol) {
error("only one address/symbol allowed");
}
symbol = optarg;
break;
case 'o':
if (offset) {
error("only one offset allowed");
}
if (!strncmp(optarg, "0x", 2)) {
sscanf(optarg, "%x", &offset);
} else {
offset = atoi(optarg);
}
break;
}
/* end while switch() */
}
if (argc > 1) {
if (addr || symbol) {
argv += optind;
argc -= optind;
if (argc < 1) {
error("No file to patch.");
}
fname = argv[0];
if ((fd = open(fname, 0)) < 0) {
error("Can't open file");
}
if (read(fd, &e, sizeof(e)) != sizeof(e)
|| N_BADMAG(e)) {
error("Not a valid executable.");
}
/* fake mid, so the N_ macros work on the amiga.. */
e.a_midmag |= 127 << 16;
if (symbol) {
struct nlist nl[2];
if (offset == 0) {
u_long new_do_replace = 0;
new_do_replace = FindAssign(symbol,
&replace);
if (new_do_replace && do_replace)
error("Cannot use both '=' "
"and '-r' option!");
FindOffset(symbol, &index);
if (size_opt) {
/* Treat like an index */
offset = index*size;
} else {
/* Treat like an offset */
offset = index;
}
if (new_do_replace)
do_replace = new_do_replace;
}
nl[0].n_un.n_name = symbol;
nl[1].n_un.n_name = 0;
if (nlist(fname, nl) != 0) {
fprintf(stderr, "Symbol is %s ",
symbol);
error("Symbol not found.");
}
addr = nl[0].n_value;
type = nl[0].n_type & N_TYPE;
} else {
type = N_UNDF;
if (addr >= N_TXTADDR(e) &&
addr < N_DATADDR(e)) {
type = N_TEXT;
} else if (addr >= N_DATADDR(e) &&
addr < N_DATADDR(e) + e.a_data) {
type = N_DATA;
}
}
addr += offset;
/*
* if replace-mode, have to reopen the file
* for writing. Can't do that from the
* beginning, or nlist() will not work (at
* least not under AmigaDOS)
*/
if (do_replace) {
close(fd);
if ((fd = open(fname, 2)) == -1) {
error("Can't reopen file for writing.");
}
}
if (type != N_TEXT && type != N_DATA) {
error("address/symbol is not in text "
"or data section.");
}
if (type == N_TEXT) {
off = addr - N_TXTADDR(e) + N_TXTOFF(e);
} else {
off = addr - N_DATADDR(e) + N_DATOFF(e);
}
if (lseek(fd, off, 0) == -1) {
error("lseek");
}
/*
* not beautiful, but works on big and little
* endian machines
*/
switch (size) {
case 1:
if (read(fd, &cval, 1) != 1) {
error("cread");
}
lval = cval;
break;
case 2:
if (read(fd, &sval, 2) != 2) {
error("sread");
}
lval = sval;
break;
case 4:
if (read(fd, &lval, 4) != 4) {
error("lread");
}
break;
}/* switch size */
if (symbol) {
printf("%s(0x%x): %d (0x%x)\n", symbol, addr,
lval, lval);
} else {
printf("0x%x: %d (0x%x)\n", addr, lval, lval);
}
if (do_replace) {
if (lseek(fd, off, 0) == -1) {
error("write-lseek");
}
switch (size) {
case 1:
cval = replace;
if (cval != replace) {
error("byte-value overflow.");
}
if (write(fd, &cval, 1) != 1) {
error("cwrite");
}
break;
case 2:
sval = replace;
if (sval != replace) {
error("word-value overflow.");
}
if (write(fd, &sval, 2) != 2) {
error("swrite");
}
break;
case 4:
if (write(fd, &replace, 4) != 4) {
error("lwrite");
}
break;
}
/* end switch(size) */
}
/* end if (do_replace) */
close(fd);
} else {
/* not (addr || symbol) */
error("Must specify either address or symbol.");
}
} else {
/* if argc <= 1 */
Synopsis(pgname);
}
return 0;
}
/* end main () */
void
error(char *str)
{
fprintf(stderr, "%s\n", str);
exit(1);
}
/* Give user very short help to avoid scrolling screen much */
static void
Synopsis(char *pgname)
{
fprintf(stdout, synusage, pgname, pgname, pgname, pgname, pgname);
}
static void
Usage(char *pgname)
{
Synopsis(pgname);
fprintf(stdout, desusage);
exit(0);
}
/*
* FindOffset() - Determine if there is an offset, -or- index
* embedded in the symbol.
*
* If there is, return it, and truncate symbol to exclude the [...].
*
* Example: If view is declared as short view[10],
* and we want to index the 3rd. element.
* which is offset = (3 -1)*sizeof(short) =4.
* we would use view[4], which becomes view,4.
*
* The way the code is implemented the [value] is
* treated as a index if-and-only-if a '-b -w -l' option
* was given. Otherwise it is treated like an offset.
* See above documentation in for of help!
*/
static void
FindOffset(char *symbol, u_long *index)
{
/* Start of '[', now line must contain matching']' */
char *sb = strchr(symbol, '[');
/* End of ']' */
char *eb = strchr(symbol, ']');
/* symbol size */
short sz = strlen(symbol);
if (sb) {
if (eb && (eb > sb)) {
if ((eb - symbol) == (sz - 1)) {
/* Start of index */
char *sindex;
u_long newindex = 0;
/*
* In the future we could get fancy
* and parse the sindex string for
* mathmatical expressions like: (3 -
* 1)*2 = 4 from above example, ugh
* forget I mentioned ot :-) !
*/
sindex = sb + 1;
*eb = '\0';
newindex = (u_long)atoi(sindex);
if (*index == 0) {
*index = newindex;
/* Make _view[3] look like _view */
*sb = '\0';
} else {
fprintf(stderr, "Error index can "
"only be specified once!\n");
}
} else {
fprintf(stderr, "Error: Garbage "
"trailing ']'\n");
}
} else {
fprintf(stderr, "Error ']' in symbol before '[' !\n");
}
}
/* end if sb != 0 */
}
/* end FindOffset */
/*
* FindAssign : Scans symbol name for an '=number' strips it off of
* the symbol and proceeds.
*/
static u_long
FindAssign(char *symbol, u_long *rvalue)
{
/* Assign symbol some number */
char *ce = rindex(symbol, '=');
/* This should point at some number, no spaces allowed */
char *cn = ce + 1;
/* flag for do_replace */
u_long dr = 0;
if (ce) {
/* number of variaables scanned in */
int nscan;
/* get the number to assign to symbol and strip off = */
for (cn=ce + 1; *cn==' '; cn++)
;
if (!strncmp(cn, "0x", 2)) {
nscan = sscanf(cn, "%x", rvalue);
} else {
nscan = sscanf(cn, "%d", rvalue);
}
if (nscan != 1) {
error("Invalid value following '='");
}
dr = 1;
/* Now were left with just symbol */
*ce = '\0';
}
/* end if (ce) */
return(dr);
}
/* end FindAssign */