memzap.c (5578B)
1 /* See LICENSE file for copyright and license details. */ 2 3 #include <libelf/libelf.h> 4 #include <libelf/gelf.h> 5 6 #include "proto.h" 7 8 static int verbose = 0; 9 static int fsymbol = 0; 10 11 static void 12 usage(const char *prog) 13 { 14 fprintf(stderr, "usage: %s [OPTIONS] prog\n", prog); 15 fprintf(stderr, " -s\tSpecify symbol to trace\n"); 16 fprintf(stderr, " -r\tSpecify region to trace\n"); 17 fprintf(stderr, " -l\tLength of traced region\n"); 18 fprintf(stderr, " -v\tEnable verbose output\n"); 19 fprintf(stderr, " -V\tVersioning info\n"); 20 fprintf(stderr, " -h\tThis help screen\n"); 21 } 22 23 static void 24 dump_base_image(const char *name, void *buf, size_t len) 25 { 26 ssize_t ret; 27 char mem_path[PATH_MAX]; 28 int fdmem; 29 30 snprintf(mem_path, sizeof(mem_path), 31 "%s.mem", name); 32 fdmem = open(mem_path, O_CREAT | O_RDWR | O_TRUNC, 0644); 33 if (fdmem < 0) 34 errx(1, "%s: %s", mem_path, strerror(errno)); 35 36 ret = write(fdmem, buf, len); 37 if ((size_t)ret != len) 38 err(1, "write"); 39 40 close(fdmem); 41 } 42 43 int 44 main(int argc, char *argv[]) 45 { 46 struct mem_region *mr_old, *mr_new; 47 struct mem_region_diff *rdiff; 48 struct mdiff_hdr hdr; 49 int c; 50 int stat; 51 pid_t pid; 52 unsigned char *buf, *buf_new; 53 char *addr = NULL; 54 size_t len = 0; 55 int fd; 56 char *prog = *argv; 57 char mdiff_path[PATH_MAX]; 58 bool once = false; 59 uint64_t cycle = 0; 60 char *symbol = NULL, *tmp; 61 Elf *e; 62 Elf32_Word symbol_count, i; 63 Elf_Scn *scn = NULL; 64 Elf_Data *edata = NULL; 65 GElf_Sym sym; 66 GElf_Shdr shdr; 67 68 while ((c = getopt(argc, argv, "hs:l:r:vV")) != -1) { 69 switch (c) { 70 case 'h': 71 usage(prog); 72 return 0; 73 case 'v': 74 verbose = 1; 75 break; 76 case 'V': 77 printf("%s\n", VERSION); 78 return 0; 79 case 'r': 80 addr = (char *)strtoul(optarg, NULL, 16); 81 break; 82 case 'l': 83 len = strtoul(optarg, NULL, 10); 84 break; 85 case 's': 86 fsymbol = 1; 87 symbol = strdup(optarg); 88 break; 89 case '?': 90 default: 91 return 1; 92 } 93 } 94 argc -= optind; 95 argv += optind; 96 97 if (argc != 1) { 98 usage(prog); 99 return 1; 100 } 101 102 if (fsymbol) { 103 if (addr || len) { 104 errx(1, "You can't trace both a symbol and a region/length"); 105 } 106 } else { 107 if (!addr || !len) { 108 errx(1, "no memory region/len specified"); 109 } 110 } 111 112 if (fsymbol) { 113 if (elf_version(EV_CURRENT) == EV_NONE) 114 errx(1, "ELF library initialization failed: %s", 115 elf_errmsg(-1)); 116 117 fd = open(*argv, O_RDONLY); 118 if (fd < 0) 119 errx(1, "%s: %s\n", *argv, strerror(errno)); 120 121 e = elf_begin(fd, ELF_C_READ, NULL); 122 if (!e) 123 errx(1, "elf_begin() failed: %s", 124 elf_errmsg(-1)); 125 126 while ((scn = elf_nextscn(e, scn))) { 127 gelf_getshdr(scn, &shdr); 128 if (shdr.sh_type == SHT_SYMTAB) { 129 edata = elf_getdata(scn, edata); 130 symbol_count = shdr.sh_size / shdr.sh_entsize; 131 for (i = 0; i < symbol_count; i++) { 132 gelf_getsym(edata, i, &sym); 133 tmp = elf_strptr(e, shdr.sh_link, sym.st_name); 134 if (!strcmp(symbol, tmp)) { 135 if (verbose > 0) { 136 printf("found symbol %s at %p\n", 137 symbol, (void *)sym.st_value); 138 } 139 addr = (void *)sym.st_value; 140 len = (size_t)sym.st_size; 141 break; 142 } 143 } 144 if (!addr || !len) 145 errx(1, "symbol %s could not be located", symbol); 146 } 147 } 148 149 free(symbol); 150 elf_end(e); 151 close(fd); 152 } 153 154 if (verbose > 0) 155 printf("base address: %p, length: %zu\n", 156 addr, len); 157 158 pid = fork(); 159 if (pid < 0) 160 err(1, "fork"); 161 162 switch (pid) { 163 case 0: 164 traceme(); 165 execl(*argv, *argv, (char *)NULL); 166 _Exit(1); 167 default: 168 break; 169 } 170 171 if (verbose > 0) 172 printf("mapping buffers into memory\n"); 173 174 buf = map_buf(len); 175 buf_new = map_buf(len); 176 177 if (verbose > 0) 178 printf("single stepping child with pid %jd\n", 179 (intmax_t)pid); 180 181 wait(&stat); 182 if (!WIFSTOPPED(stat)) 183 goto out_mmap; 184 single_step(pid); 185 wait(&stat); 186 if (!WIFSTOPPED(stat)) 187 goto out_mmap; 188 cycle++; 189 190 /* Create .mdiff file and initialize the header */ 191 snprintf(mdiff_path, sizeof(mdiff_path), 192 "%s.mdiff", *argv); 193 fd = create_mdiff(mdiff_path); 194 memset(&hdr, 0, sizeof(hdr)); 195 strncpy((char *)hdr.magic, "MDIFF", 5); 196 hdr.blksize = BLKSIZE; 197 hdr.endianness = 0; 198 hdr.version = 1; 199 hdr.rsize = len; 200 mdiff_start_diff(fd); 201 202 /* Trace the child process and diff the memory regions 203 * per instruction */ 204 do { 205 /* Read in the Nth generation of the traced region */ 206 readmem(pid, buf, addr, len); 207 if (!once) { 208 dump_base_image(*argv, buf, len); 209 once = true; 210 } 211 212 /* Build a memory region that tracks this buffer */ 213 mr_old = build_mem_region(buf, len); 214 if (!mr_old) 215 errx(1, "failed to build memory region\n"); 216 217 /* Single step the child in order to get the next 218 * generation */ 219 single_step(pid); 220 wait(&stat); 221 if (!WIFSTOPPED(stat)) { 222 free_mem_region(mr_old); 223 break; 224 } 225 cycle++; 226 227 /* Read in the (N + 1)th generation of the traced 228 * region */ 229 readmem(pid, buf_new, addr, len); 230 231 /* Build a memory region that tracks this buffer */ 232 mr_new = build_mem_region(buf_new, len); 233 if (!mr_new) 234 errx(1, "failed to build memory region\n"); 235 236 /* Diff the original copy with the updated copy */ 237 rdiff = diff_mem_region(mr_old, mr_new); 238 if (rdiff->nmrdiffs) { 239 /* If we've found differences, then apply them on 240 * the Nth generation */ 241 apply_diff(mr_old, rdiff); 242 hdr.nregions += mdiff_append_rdiff(fd, rdiff, cycle); 243 } 244 245 /* Free these and go up for another run */ 246 free_mem_region(mr_old); 247 free_mem_region(mr_new); 248 free_mem_region_diff(rdiff); 249 } while(1); 250 251 /* Insert header at the start of the .mdiff file */ 252 mdiff_insert_hdr(fd, &hdr); 253 close_mdiff(fd); 254 out_mmap: 255 unmap_buf(buf, len); 256 unmap_buf(buf_new, len); 257 return 0; 258 }