|
| 1 | +#include "tracewrap.h" |
| 2 | +#include "trace_consts.h" |
| 3 | +#include "exec/cpu_ldst.h" |
| 4 | + |
| 5 | +#include <glib.h> |
| 6 | +#include <err.h> |
| 7 | +#include "qemu/log.h" |
| 8 | + |
| 9 | +#include <sys/types.h> |
| 10 | +#include <unistd.h> |
| 11 | +#include <sys/stat.h> |
| 12 | + |
| 13 | +#include <limits.h> |
| 14 | +#include <stdlib.h> |
| 15 | + |
| 16 | + |
| 17 | +char tracer_name[] = "qemu"; |
| 18 | +char tracer_version[] = "2.0.0/tracewrap"; |
| 19 | + |
| 20 | +static Frame * g_frame; |
| 21 | +static uint64_t frames_per_toc_entry = 64LL; |
| 22 | +static uint32_t open_frame = 0; |
| 23 | +static FILE *file = NULL; |
| 24 | + |
| 25 | +/* don't use the following data directly! |
| 26 | + use toc_init, toc_update and toc_write functions instead */ |
| 27 | +static uint64_t *toc = NULL; |
| 28 | +static int toc_entries = 0; |
| 29 | +static int toc_capacity = 0; |
| 30 | +static uint64_t toc_num_frames = 0; |
| 31 | + |
| 32 | +#define MD5LEN 16 |
| 33 | +static guchar target_md5[MD5LEN]; |
| 34 | +static char target_path[PATH_MAX] = "unknown"; |
| 35 | + |
| 36 | + |
| 37 | +#define WRITE(x) do { \ |
| 38 | + if (!file) \ |
| 39 | + err(1, "qemu_trace is not initialized"); \ |
| 40 | + if (fwrite(&(x), sizeof(x),1,file) != 1) \ |
| 41 | + err(1, "fwrite failed"); \ |
| 42 | + } while(0) |
| 43 | + |
| 44 | +#define WRITE_BUF(x,n) do { \ |
| 45 | + if (!file) \ |
| 46 | + err(1, "qemu_trace is not initialized"); \ |
| 47 | + if (fwrite((x),1,(n),file) != n) \ |
| 48 | + err(1, "fwrite failed"); \ |
| 49 | + } while(0) |
| 50 | + |
| 51 | +#define SEEK(off) do { \ |
| 52 | + if (fseek(file,(off), SEEK_SET) < 0) \ |
| 53 | + err(1, "stream not seekable"); \ |
| 54 | + } while(0) |
| 55 | + |
| 56 | + |
| 57 | +static void toc_init(void) { |
| 58 | + if (toc_entries != 0) |
| 59 | + err(1, "qemu_trace was initialized twice"); |
| 60 | + toc = g_new(uint64_t, 1024); |
| 61 | + toc_capacity = 1024; |
| 62 | + toc_entries = 0; |
| 63 | +} |
| 64 | + |
| 65 | +static void toc_append(uint64_t entry) { |
| 66 | + if (toc_capacity <= toc_entries) { |
| 67 | + toc = g_renew(uint64_t, toc, toc_capacity * 2); |
| 68 | + toc_capacity *= 2; |
| 69 | + } |
| 70 | + toc[toc_entries++] = entry; |
| 71 | +} |
| 72 | + |
| 73 | +static void toc_write(void) { |
| 74 | + int64_t toc_offset = ftell(file); |
| 75 | + if (toc_offset > 0) { |
| 76 | + int i = 0; |
| 77 | + WRITE(frames_per_toc_entry); |
| 78 | + for (i = 0; i < toc_entries; i++) |
| 79 | + WRITE(toc[i]); |
| 80 | + SEEK(num_trace_frames_offset); |
| 81 | + WRITE(toc_num_frames); |
| 82 | + SEEK(toc_offset_offset); |
| 83 | + WRITE(toc_offset); |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +static void toc_update(void) { |
| 88 | + toc_num_frames++; |
| 89 | + if (toc_num_frames % frames_per_toc_entry == 0) { |
| 90 | + int64_t off = ftell(file); |
| 91 | + if (off >= 0) toc_append(off); |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +static void write_header(void) { |
| 96 | + uint64_t toc_off = 0L; |
| 97 | + WRITE(magic_number); |
| 98 | + WRITE(out_trace_version); |
| 99 | + WRITE(frame_arch); |
| 100 | + WRITE(frame_mach); |
| 101 | + WRITE(toc_num_frames); |
| 102 | + WRITE(toc_off); |
| 103 | +} |
| 104 | + |
| 105 | +static int list_length(char **list) { |
| 106 | + int n=0; |
| 107 | + if (list) { |
| 108 | + char **p = list; |
| 109 | + for (;*p;p++,n++); |
| 110 | + } |
| 111 | + return n; |
| 112 | +} |
| 113 | + |
| 114 | +static void compute_target_md5(void) { |
| 115 | + const GChecksumType md5 = G_CHECKSUM_MD5; |
| 116 | + GChecksum *cs = g_checksum_new(md5); |
| 117 | + FILE *target = fopen(target_path, "r"); |
| 118 | + guchar buf[BUFSIZ]; |
| 119 | + gsize expected_length = MD5LEN; |
| 120 | + |
| 121 | + if (!cs) err(1, "failed to create a checksum"); |
| 122 | + if (!target) err(1, "failed to open target binary"); |
| 123 | + if (g_checksum_type_get_length(md5) != expected_length) abort(); |
| 124 | + |
| 125 | + while (!feof(target)) { |
| 126 | + size_t len = fread(buf,1,BUFSIZ,target); |
| 127 | + if (ferror(target)) |
| 128 | + err(1, "failed to read target binary"); |
| 129 | + g_checksum_update(cs, buf, len); |
| 130 | + } |
| 131 | + |
| 132 | + g_checksum_get_digest(cs, target_md5, &expected_length); |
| 133 | + fclose(target); |
| 134 | +} |
| 135 | + |
| 136 | +static void store_to_trace(ProtobufCBuffer *self, size_t len, const uint8_t *data) { |
| 137 | + WRITE_BUF(data,len); |
| 138 | +} |
| 139 | + |
| 140 | +static void init_tracer(Tracer *tracer, char **argv, char **envp) { |
| 141 | + tracer__init(tracer); |
| 142 | + tracer->name = tracer_name; |
| 143 | + tracer->n_args = list_length(argv); |
| 144 | + tracer->args = argv; |
| 145 | + tracer->n_envp = list_length(envp); |
| 146 | + tracer->envp = envp; |
| 147 | + tracer->version = tracer_version; |
| 148 | +} |
| 149 | + |
| 150 | +static void init_target(Target *target, char **argv, char **envp) { |
| 151 | + compute_target_md5(); |
| 152 | + |
| 153 | + target__init(target); |
| 154 | + target->path = target_path; |
| 155 | + target->n_args = list_length(argv); |
| 156 | + target->args = argv; |
| 157 | + target->n_envp = list_length(envp); |
| 158 | + target->envp = envp; |
| 159 | + target->md5sum.len = MD5LEN; |
| 160 | + target->md5sum.data = target_md5; |
| 161 | +} |
| 162 | + |
| 163 | +#ifdef G_OS_UNIX |
| 164 | +static void unix_fill_fstats(Fstats *fstats, char *path) { |
| 165 | + struct stat stats; |
| 166 | + if (stat(path, &stats) < 0) |
| 167 | + err(1, "failed to obtain file stats"); |
| 168 | + |
| 169 | + fstats->size = stats.st_size; |
| 170 | + fstats->atime = stats.st_atime; |
| 171 | + fstats->mtime = stats.st_mtime; |
| 172 | + fstats->ctime = stats.st_ctime; |
| 173 | +} |
| 174 | +#endif |
| 175 | + |
| 176 | + |
| 177 | +static void init_fstats(Fstats *fstats) { |
| 178 | + fstats__init(fstats); |
| 179 | +#ifdef G_OS_UNIX |
| 180 | + unix_fill_fstats(fstats, target_path); |
| 181 | +#endif |
| 182 | +} |
| 183 | + |
| 184 | + |
| 185 | +static void write_meta( |
| 186 | + char **tracer_argv, |
| 187 | + char **tracer_envp, |
| 188 | + char **target_argv, |
| 189 | + char **target_envp) |
| 190 | +{ |
| 191 | + MetaFrame meta; |
| 192 | + Tracer tracer; |
| 193 | + Target target; |
| 194 | + Fstats fstats; |
| 195 | + ProtobufCBuffer buffer; |
| 196 | + |
| 197 | + buffer.append = store_to_trace; |
| 198 | + |
| 199 | + |
| 200 | + meta_frame__init(&meta); |
| 201 | + init_tracer(&tracer, tracer_argv, tracer_envp); |
| 202 | + init_target(&target, target_argv, target_envp); |
| 203 | + init_fstats(&fstats); |
| 204 | + |
| 205 | + meta.tracer = &tracer; |
| 206 | + meta.target = ⌖ |
| 207 | + meta.fstats = &fstats; |
| 208 | + meta.time = time(NULL); |
| 209 | + char *user = g_strdup(g_get_real_name()); |
| 210 | + meta.user = user; |
| 211 | + |
| 212 | + char *host = g_strdup(g_get_host_name()); |
| 213 | + meta.host = host; |
| 214 | + |
| 215 | + uint64_t size = meta_frame__get_packed_size(&meta); |
| 216 | + WRITE(size); |
| 217 | + |
| 218 | + meta_frame__pack_to_buffer(&meta, &buffer); |
| 219 | + |
| 220 | + free(user); |
| 221 | + free(host); |
| 222 | +} |
| 223 | + |
| 224 | + |
| 225 | +void qemu_trace_init(const char *filename, |
| 226 | + const char *targetname, |
| 227 | + char **argv, char **envp, |
| 228 | + char **target_argv, |
| 229 | + char **target_envp) { |
| 230 | + qemu_log("Initializing tracer\n"); |
| 231 | + if (realpath(targetname,target_path) == NULL) |
| 232 | + err(1, "can't get target path"); |
| 233 | + |
| 234 | + |
| 235 | + char *name = filename |
| 236 | + ? g_strdup(filename) |
| 237 | + : g_strdup_printf("%s.frames", basename(target_path)); |
| 238 | + file = fopen(name, "wb"); |
| 239 | + if (file == NULL) |
| 240 | + err(1, "tracewrap: can't open trace file %s", name); |
| 241 | + write_header(); |
| 242 | + write_meta(argv, envp, target_argv, target_envp); |
| 243 | + toc_init(); |
| 244 | + g_free(name); |
| 245 | +} |
| 246 | + |
| 247 | + |
| 248 | +void qemu_trace_newframe(target_ulong addr, int __unused/*thread_id*/ ) { |
| 249 | + int thread_id = 1; |
| 250 | + if (open_frame) { |
| 251 | + qemu_log("frame is still open"); |
| 252 | + qemu_trace_endframe(NULL, 0, 0); |
| 253 | + } |
| 254 | + |
| 255 | + open_frame = 1; |
| 256 | + g_frame = g_new(Frame,1); |
| 257 | + frame__init(g_frame); |
| 258 | + |
| 259 | + StdFrame *sframe = g_new(StdFrame, 1); |
| 260 | + std_frame__init(sframe); |
| 261 | + g_frame->std_frame = sframe; |
| 262 | + |
| 263 | + sframe->address = addr; |
| 264 | + sframe->thread_id = thread_id; |
| 265 | + |
| 266 | + OperandValueList *ol_in = g_new(OperandValueList,1); |
| 267 | + operand_value_list__init(ol_in); |
| 268 | + ol_in->n_elem = 0; |
| 269 | + sframe->operand_pre_list = ol_in; |
| 270 | + |
| 271 | + OperandValueList *ol_out = g_new(OperandValueList,1); |
| 272 | + operand_value_list__init(ol_out); |
| 273 | + ol_out->n_elem = 0; |
| 274 | + sframe->operand_post_list = ol_out; |
| 275 | +} |
| 276 | + |
| 277 | +static inline void free_operand(OperandInfo *oi) { |
| 278 | + OperandInfoSpecific *ois = oi->operand_info_specific; |
| 279 | + |
| 280 | + //Free reg-operand |
| 281 | + RegOperand *ro = ois->reg_operand; |
| 282 | + if (ro && ro->name) |
| 283 | + g_free(ro->name); |
| 284 | + g_free(ro); |
| 285 | + |
| 286 | + //Free mem-operand |
| 287 | + MemOperand *mo = ois->mem_operand; |
| 288 | + g_free(mo); |
| 289 | + g_free(oi->value.data); |
| 290 | + g_free(oi->taint_info); |
| 291 | + g_free(ois); |
| 292 | + g_free(oi->operand_usage); |
| 293 | + g_free(oi); |
| 294 | +} |
| 295 | + |
| 296 | +void qemu_trace_add_operand(OperandInfo *oi, int inout) { |
| 297 | + if (!open_frame) { |
| 298 | + if (oi) |
| 299 | + free_operand(oi); |
| 300 | + return; |
| 301 | + } |
| 302 | + OperandValueList *ol; |
| 303 | + if (inout & 0x1) { |
| 304 | + ol = g_frame->std_frame->operand_pre_list; |
| 305 | + } else { |
| 306 | + ol = g_frame->std_frame->operand_post_list; |
| 307 | + } |
| 308 | + |
| 309 | + oi->taint_info = g_new(TaintInfo, 1); |
| 310 | + taint_info__init(oi->taint_info); |
| 311 | + oi->taint_info->no_taint = 1; |
| 312 | + oi->taint_info->has_no_taint = 1; |
| 313 | + |
| 314 | + ol->n_elem += 1; |
| 315 | + ol->elem = g_renew(OperandInfo *, ol->elem, ol->n_elem); |
| 316 | + ol->elem[ol->n_elem - 1] = oi; |
| 317 | +} |
| 318 | + |
| 319 | +void qemu_trace_endframe(CPUArchState *env, target_ulong pc, target_ulong size) { |
| 320 | + int i = 0; |
| 321 | + StdFrame *sframe = g_frame->std_frame; |
| 322 | + |
| 323 | + if (!open_frame) return; |
| 324 | + |
| 325 | + sframe->rawbytes.len = size; |
| 326 | + sframe->rawbytes.data = g_malloc(size); |
| 327 | + for (i = 0; i < size; i++) { |
| 328 | + sframe->rawbytes.data[i] = cpu_ldub_code(env, pc+i); |
| 329 | + } |
| 330 | + |
| 331 | + size_t msg_size = frame__get_packed_size(g_frame); |
| 332 | + uint8_t *packed_buffer = g_alloca(msg_size); |
| 333 | + uint64_t packed_size = frame__pack(g_frame, packed_buffer); |
| 334 | + WRITE(packed_size); |
| 335 | + WRITE_BUF(packed_buffer, packed_size); |
| 336 | + toc_update(); |
| 337 | + |
| 338 | + //counting num_frames in newframe does not work by far ... |
| 339 | + //how comes? disas_arm_insn might not always return at the end? |
| 340 | + for (i = 0; i < sframe->operand_pre_list->n_elem; i++) |
| 341 | + free_operand(sframe->operand_pre_list->elem[i]); |
| 342 | + g_free(sframe->operand_pre_list->elem); |
| 343 | + g_free(sframe->operand_pre_list); |
| 344 | + |
| 345 | + for (i = 0; i < sframe->operand_post_list->n_elem; i++) |
| 346 | + free_operand(sframe->operand_post_list->elem[i]); |
| 347 | + g_free(sframe->operand_post_list->elem); |
| 348 | + g_free(sframe->operand_post_list); |
| 349 | + |
| 350 | + g_free(sframe->rawbytes.data); |
| 351 | + g_free(sframe); |
| 352 | + g_free(g_frame); |
| 353 | + open_frame = 0; |
| 354 | +} |
| 355 | + |
| 356 | +void qemu_trace_finish(uint32_t exit_code) { |
| 357 | + toc_write(); |
| 358 | + if (fclose(file) != 0) |
| 359 | + err(1,"failed to write trace file, the file maybe corrupted"); |
| 360 | +} |
0 commit comments