Home | History | Annotate | Line # | Download | only in binpatch
binpatch.c revision 1.6
      1 /* $NetBSD: binpatch.c,v 1.6 2009/08/20 15:14:49 tsutsui Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2009 Izumi Tsutsui.  All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 /*
     28  * Copyright (c) 1996 Christopher G. Demetriou
     29  * All rights reserved.
     30  *
     31  * Redistribution and use in source and binary forms, with or without
     32  * modification, are permitted provided that the following conditions
     33  * are met:
     34  * 1. Redistributions of source code must retain the above copyright
     35  *    notice, this list of conditions and the following disclaimer.
     36  * 2. Redistributions in binary form must reproduce the above copyright
     37  *    notice, this list of conditions and the following disclaimer in the
     38  *    documentation and/or other materials provided with the distribution.
     39  * 3. The name of the author may not be used to endorse or promote products
     40  *    derived from this software without specific prior written permission.
     41  *
     42  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     43  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     44  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     45  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     46  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     47  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     48  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     49  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     50  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     51  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     52  *
     53  * <<Id: LICENSE_GC,v 1.1 2001/10/01 23:24:05 cgd Exp>>
     54  */
     55 
     56 #include <sys/cdefs.h>
     57 #ifndef lint
     58 __COPYRIGHT("@(#) Copyright (c) 1996\
     59  Christopher G. Demetriou.  All rights reserved.");
     60 #endif /* not lint */
     61 
     62 #ifndef lint
     63 __RCSID("$NetBSD: binpatch.c,v 1.6 2009/08/20 15:14:49 tsutsui Exp $");
     64 #endif /* not lint */
     65 
     66 #include <sys/types.h>
     67 #include <sys/mman.h>
     68 #include <sys/stat.h>
     69 #include <sys/inttypes.h>
     70 
     71 #include <err.h>
     72 #include <fcntl.h>
     73 #include <limits.h>
     74 #include <nlist.h>
     75 #include <stdio.h>
     76 #include <stdlib.h>
     77 #include <stdbool.h>
     78 #include <unistd.h>
     79 
     80 #include "extern.h"
     81 
     82 int		main(int, char *[]);
     83 static void	usage(void) __dead;
     84 
     85 bool replace, verbose;
     86 u_long addr, offset;
     87 char *symbol;
     88 size_t size;
     89 uint64_t val;
     90 
     91 #ifdef NLIST_AOUT
     92 /*
     93  * Since we can't get the text address from an a.out executable, we
     94  * need to be able to specify it.  Note: there's no way to test to
     95  * see if the user entered a valid address!
     96  */
     97 int	T_flag_specified;	/* the -T flag was specified */
     98 u_long	text_start;		/* Start of kernel text */
     99 #endif /* NLIST_AOUT */
    100 
    101 static const struct {
    102 	const char *name;
    103 	int	(*check)(const char *, size_t);
    104 	int	(*findoff)(const char *, size_t, u_long, size_t *);
    105 } exec_formats[] = {
    106 #ifdef NLIST_AOUT
    107 	{	"a.out",	check_aout,	findoff_aout,	},
    108 #endif
    109 #ifdef NLIST_ECOFF
    110 	{	"ECOFF",	check_ecoff,	findoff_ecoff,	},
    111 #endif
    112 #ifdef NLIST_ELF32
    113 	{	"ELF32",	check_elf32,	findoff_elf32,	},
    114 #endif
    115 #ifdef NLIST_ELF64
    116 	{	"ELF64",	check_elf64,	findoff_elf64,	},
    117 #endif
    118 #ifdef NLIST_COFF
    119 	{	"COFF",		check_coff,	findoff_coff,	},
    120 #endif
    121 };
    122 
    123 
    124 int
    125 main(int argc, char *argv[])
    126 {
    127 	const char *fname;
    128 	struct stat sb;
    129 	struct nlist nl[2];
    130 	char *mappedfile;
    131 	size_t valoff;
    132 	void *valp;
    133 	uint8_t uval8;
    134 	int8_t  sval8;
    135 	uint16_t uval16;
    136 	int16_t  sval16;
    137 	uint32_t uval32;
    138 	int32_t  sval32;
    139 	uint64_t uval64;
    140 	int64_t  sval64;
    141 	int ch, fd, rv, i, n;
    142 
    143 	setprogname(argv[0]);
    144 
    145 	while ((ch = getopt(argc, argv, "bwldT:a:s:o:r:v")) != -1)
    146 		switch (ch) {
    147 		case 'b':
    148 			size = sizeof(uint8_t);
    149 			break;
    150 		case 'w':
    151 			size = sizeof(uint16_t);
    152 			break;
    153 		case 'l':
    154 			size = sizeof(uint32_t);
    155 			break;
    156 		case 'd':
    157 			size = sizeof(uint64_t);
    158 			break;
    159 		case 'a':
    160 			if (addr != 0 || symbol != NULL)
    161 				errx(EXIT_FAILURE,
    162 				    "only one address/symbol allowed");
    163 			addr = strtoul(optarg, NULL, 0);
    164 			break;
    165 		case 's':
    166 			if (addr != 0 || symbol != NULL)
    167 				errx(EXIT_FAILURE,
    168 				    "only one address/symbol allowed");
    169 			symbol = optarg;
    170 			break;
    171 		case 'o':
    172 			if (offset != 0)
    173 				err(EXIT_FAILURE,
    174 				    "only one offset allowed");
    175 			offset = strtoul(optarg, NULL, 0);
    176 			break;
    177 		case 'r':
    178 			replace = true;
    179 			val = strtoull(optarg, NULL, 0);
    180 			break;
    181 		case 'v':
    182 			verbose = true;
    183 			break;
    184 		case 'T':
    185 #ifdef NLIST_AOUT
    186 			T_flag_specified = 1;
    187 			text_start = strtoul(optarg, NULL, 0);
    188 			break;
    189 #else
    190 			fprintf(stderr, "%s: unknown option -- %c\n",
    191 			    getprogname(), (char)ch);
    192 			/*FALLTHROUGH*/
    193 #endif /* NLIST_AOUT */
    194 		case '?':
    195 		default:
    196 			usage();
    197 	}
    198 	argc -= optind;
    199 	argv += optind;
    200 
    201 	if (argc != 1)
    202 		usage();
    203 
    204 	if (addr == 0 && symbol == NULL) {
    205 		warnx("no address or symbol specified");
    206 		usage();
    207 	}
    208 
    209 	if (size == 0)
    210 		size = sizeof(uint32_t);	/* default to int */
    211 
    212 	fname = argv[0];
    213 
    214 	if ((fd = open(fname, replace ? O_RDWR : O_RDONLY, 0))  == -1)
    215 		err(EXIT_FAILURE, "open %s", fname);
    216 
    217 	if (symbol != NULL) {
    218 		nl[0].n_name = symbol;
    219 		nl[1].n_name = NULL;
    220 		if ((rv = __fdnlist(fd, nl)) != 0)
    221 			errx(EXIT_FAILURE, "could not find symbol %s in %s",
    222 			    symbol, fname);
    223 		addr = nl[0].n_value;
    224 		if (verbose)
    225 			fprintf(stderr, "got symbol address 0x%lx from %s\n",
    226 			    addr, fname);
    227 	}
    228 
    229 	addr += offset * size;
    230 
    231 	if (fstat(fd, &sb) == -1)
    232 		err(EXIT_FAILURE, "fstat %s", fname);
    233 	if (sb.st_size != (ssize_t)sb.st_size)
    234 		errx(EXIT_FAILURE, "%s too big to map", fname);
    235 
    236 	if ((mappedfile = mmap(NULL, sb.st_size,
    237 	    replace ? PROT_READ | PROT_WRITE : PROT_READ,
    238 	    MAP_FILE | MAP_SHARED, fd, 0)) == (char *)-1)
    239 		err(EXIT_FAILURE, "mmap %s", fname);
    240 	if (verbose)
    241 		fprintf(stderr, "mapped %s\n", fname);
    242 
    243 	n = __arraycount(exec_formats);
    244 	for (i = 0; i < n; i++) {
    245 		if ((*exec_formats[i].check)(mappedfile, sb.st_size) == 0)
    246 			break;
    247 	}
    248 	if (i == n)
    249 		errx(EXIT_FAILURE, "%s: unknown executable format", fname);
    250 
    251 	if (verbose) {
    252 		fprintf(stderr, "%s is an %s binary\n", fname,
    253 		    exec_formats[i].name);
    254 #ifdef NLIST_AOUT
    255 		if (T_flag_specified)
    256 			fprintf(stderr, "kernel text loads at 0x%lx\n",
    257 			    text_start);
    258 #endif
    259 	}
    260 
    261 	if ((*exec_formats[i].findoff)(mappedfile, sb.st_size,
    262 	    addr, &valoff) != 0)
    263 		errx(EXIT_FAILURE, "couldn't find file offset for %s in %s",
    264 		    symbol != NULL ? nl[0].n_name : "address" , fname);
    265 
    266 	valp = mappedfile + valoff;
    267 
    268 	if (symbol)
    269 		printf("%s(0x%lx): ", symbol, addr);
    270 	else
    271 		printf("0x%lx: ", addr);
    272 
    273 	switch (size) {
    274 	case sizeof(uint8_t):
    275 		uval8 = *(uint8_t *)valp;
    276 		sval8 = *(int8_t *)valp;
    277 		printf("0x%02" PRIx8 " (%" PRIu8, uval8, uval8);
    278 		if (sval8 < 0)
    279 			printf("/%" PRId8, sval8);
    280 		printf(")");
    281 		break;
    282 	case sizeof(uint16_t):
    283 		uval16 = *(uint16_t *)valp;
    284 		sval16 = *(int16_t *)valp;
    285 		printf("0x%04" PRIx16 " (%" PRIu16, uval16, uval16);
    286 		if (sval16 < 0)
    287 			printf("/%" PRId16, sval16);
    288 		printf(")");
    289 		break;
    290 	case sizeof(uint32_t):
    291 		uval32 = *(uint32_t *)valp;
    292 		sval32 = *(int32_t *)valp;
    293 		printf("0x%08" PRIx32 " (%" PRIu32, uval32, uval32);
    294 		if (sval32 < 0)
    295 			printf("/%" PRId32, sval32);
    296 		printf(")");
    297 		break;
    298 	case sizeof(uint64_t):
    299 		uval64 = *(uint64_t *)valp;
    300 		sval64 = *(int64_t *)valp;
    301 		printf("0x%016" PRIx64 " (%" PRIu64, uval64, uval64);
    302 		if (sval64 < 0)
    303 			printf("/%" PRId64, sval64);
    304 		printf(")");
    305 		break;
    306 	}
    307 	printf(", at offset %#lx in %s\n", (unsigned long)valoff, fname);
    308 
    309 	if (!replace)
    310 		goto done;
    311 
    312 	printf("new value: ");
    313 
    314 	switch (size) {
    315 	case sizeof(uint8_t):
    316 		uval8 = (uint8_t)val;
    317 		sval8 = (int8_t)val;
    318 		printf("0x%02" PRIx8 " (%" PRIu8, uval8, uval8);
    319 		if (sval8 < 0)
    320 			printf("/%" PRId8, sval8);
    321 		printf(")");
    322 		*(uint8_t *)valp = uval8;
    323 		break;
    324 	case sizeof(uint16_t):
    325 		uval16 = (uint16_t)val;
    326 		sval16 = (int16_t)val;
    327 		printf("0x%04" PRIx16 " (%" PRIu16, uval16, uval16);
    328 		if (sval16 < 0)
    329 			printf("/%" PRId16, sval16);
    330 		printf(")");
    331 		*(uint16_t *)valp = uval16;
    332 		break;
    333 	case sizeof(uint32_t):
    334 		uval32 = (uint32_t)val;
    335 		sval32 = (int32_t)val;
    336 		printf("0x%08" PRIx32 " (%" PRIu32, uval32, uval32);
    337 		if (sval32 < 0)
    338 			printf("/%" PRId32, sval32);
    339 		printf(")");
    340 		*(uint32_t *)valp = uval32;
    341 		break;
    342 	case sizeof(uint64_t):
    343 		uval64 = (uint64_t)val;
    344 		sval64 = (int64_t)val;
    345 		printf("0x%016" PRIx64 " (%" PRIu64, uval64, uval64);
    346 		if (sval64 < 0)
    347 			printf("/%" PRId64, sval64);
    348 		printf(")");
    349 		*(uint64_t *)valp = uval64;
    350 		break;
    351 	}
    352 	printf("\n");
    353 
    354  done:
    355 	munmap(mappedfile, sb.st_size);
    356 	close(fd);
    357 
    358 	if (verbose)
    359 		fprintf(stderr, "exiting\n");
    360 	exit(EXIT_SUCCESS);
    361 }
    362 
    363 static void
    364 usage(void)
    365 {
    366 
    367 	fprintf(stderr,
    368 	    "usage: %s [-b|-w|-l|-d] [-a address | -s symbol] [-o offset]\n"
    369 	    "                [-r value] "
    370 #ifdef NLIST_AOUT
    371 	    "[-T text_start] "
    372 #endif
    373 	    "[-v] binary\n", getprogname());
    374 	exit(EXIT_FAILURE);
    375 }
    376