1 /* Copyright (c) 2008-2010, Google Inc. 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Neither the name of Google Inc. nor the names of its 11 * contributors may be used to endorse or promote products derived from 12 * this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 * 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 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 // This file is part of ThreadSanitizer, a dynamic data race detector. 28 29 // Some parts of the code in this file are taken from the examples 30 // in DynamoRIO distribution, which have the following copyright. 31 /* ********************************************************** 32 * Copyright (c) 2003-2008 VMware, Inc. All rights reserved. 33 * **********************************************************/ 34 35 /* 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions are met: 38 * 39 * * Redistributions of source code must retain the above copyright notice, 40 * this list of conditions and the following disclaimer. 41 * 42 * * Redistributions in binary form must reproduce the above copyright notice, 43 * this list of conditions and the following disclaimer in the documentation 44 * and/or other materials provided with the distribution. 45 * 46 * * Neither the name of VMware, Inc. nor the names of its contributors may be 47 * used to endorse or promote products derived from this software without 48 * specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 51 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE 54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 56 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 57 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 60 * DAMAGE. 61 */ 62 63 // Author: Konstantin Serebryany. 64 // Author: Timur Iskhodzhanov. 65 // 66 // ******* WARNING ******** 67 // This code is experimental. Do not expect anything here to work. 68 // ***** END WARNING ****** 69 70 #include "dr_api.h" 71 72 #include "ts_util.h" 73 74 #define EXTRA_REPLACE_PARAMS 75 #define REPORT_READ_RANGE(a,b) 76 #define REPORT_WRITE_RANGE(a,b) 77 #include "ts_replace.h" 78 79 #define Printf dr_printf 80 81 static void *g_lock; 82 static int g_n_created_threads; 83 84 typedef unordered_map<intptr_t, string> SymbolsTable; 85 static SymbolsTable *sym_tab; 86 87 string *g_main_module_path; 88 89 //--------------- StackFrame ----------------- {{{1 90 struct StackFrame { 91 uintptr_t pc; 92 uintptr_t sp; 93 StackFrame(uintptr_t p, uintptr_t s) : pc(p), sp(s) { } 94 }; 95 96 97 //--------------- DrThread ----------------- {{{1 98 struct DrThread { 99 int tid; // A unique 0-based thread id. 100 vector<StackFrame> shadow_stack; 101 }; 102 103 static DrThread &GetCurrentThread(void *drcontext) { 104 return *(DrThread*)dr_get_tls_field(drcontext); 105 } 106 107 //--------------- ShadowStack ----------------- {{{1 108 #define DEB_PR (0 && t.tid == 1) 109 110 static void PrintShadowStack(DrThread &t) { 111 Printf("T%d Shadow stack (%d)\n", t.tid, (int)t.shadow_stack.size()); 112 for (int i = t.shadow_stack.size() - 1; i >= 0; i--) { 113 uintptr_t pc = t.shadow_stack[i].pc; 114 Printf("%s[%p]\n", g_main_module_path->c_str(), pc); 115 } 116 for (int i = t.shadow_stack.size() - 1; i >= 0; i--) { 117 uintptr_t pc = t.shadow_stack[i].pc; 118 uintptr_t sp = t.shadow_stack[i].sp; 119 Printf(" sp=%p pc=%p\n", sp, pc); 120 } 121 } 122 123 static void UpdateShadowStack(DrThread &t, uintptr_t sp) { 124 while (t.shadow_stack.size() > 0 && sp >= t.shadow_stack.back().sp) { 125 t.shadow_stack.pop_back(); 126 if (DEB_PR) { 127 dr_mutex_lock(g_lock); 128 Printf("T%d PopShadowStack\n", t.tid); 129 PrintShadowStack(t); 130 dr_mutex_unlock(g_lock); 131 } 132 } 133 } 134 135 static void PushShadowStack(DrThread &t, uintptr_t pc, uintptr_t target_pc, uintptr_t sp) { 136 if (t.shadow_stack.size() > 0) { 137 t.shadow_stack.back().pc = pc; 138 } 139 t.shadow_stack.push_back(StackFrame(target_pc, sp)); 140 if (DEB_PR) { 141 dr_mutex_lock(g_lock); 142 Printf("T%d PushShadowStack %p %p %d\n", t.tid, pc, target_pc, sp); 143 PrintShadowStack(t); 144 dr_mutex_unlock(g_lock); 145 } 146 } 147 148 //--------------- callbacks ----------------- {{{1 149 static void OnEvent_ThreadInit(void *drcontext) { 150 DrThread *t_ptr = new DrThread; 151 DrThread &t = *t_ptr; 152 153 dr_mutex_lock(g_lock); 154 t.tid = g_n_created_threads++; 155 dr_mutex_unlock(g_lock); 156 157 dr_set_tls_field(drcontext, t_ptr); 158 159 dr_printf("T%d %s\n", t.tid, (char*)__FUNCTION__+8); 160 } 161 162 static void OnEvent_ThreadExit(void *drcontext) { 163 DrThread &t = GetCurrentThread(drcontext); 164 dr_printf("T%d %s\n", t.tid, (char*)__FUNCTION__+8); 165 } 166 167 void OnEvent_ModuleLoaded(void *drcontext, const module_data_t *info, 168 bool loaded) { 169 // if this assertion fails, your DynamoRIO is too old. You need rev261 with some patches... 170 CHECK(info->full_path); 171 172 dr_printf("%s: %s (%s)\n", __FUNCTION__, 173 dr_module_preferred_name(info), info->full_path); 174 if (g_main_module_path == NULL) { 175 g_main_module_path = new string(info->full_path); 176 } 177 } 178 179 static void OnEvent_Exit(void) { 180 dr_printf("ThreadSanitizerDynamoRio: done\n"); 181 dr_mutex_destroy(g_lock); 182 } 183 184 static void On_Mop(uintptr_t pc, size_t size, void *a, bool is_w) { 185 void *drcontext = dr_get_current_drcontext(); 186 DrThread &t = GetCurrentThread(drcontext); 187 if (t.tid == 777) { 188 dr_fprintf(STDERR, "T%d pc=%p a=%p size=%ld %s\n", t.tid, pc, a, size, is_w ? "WRITE" : "READ"); 189 } 190 } 191 192 static void On_Read(uintptr_t pc, size_t size, void *a) { 193 On_Mop(pc, size, a, false); 194 } 195 196 static void On_Write(uintptr_t pc, size_t size, void *a) { 197 On_Mop(pc, size, a, true); 198 } 199 200 static void On_AnyCall(uintptr_t pc, uintptr_t target_pc, uintptr_t sp, bool is_direct) { 201 void *drcontext = dr_get_current_drcontext(); 202 DrThread &t = GetCurrentThread(drcontext); 203 // dr_fprintf(STDOUT, "T%d CALL %p => %p; sp=%p\n", t.tid, pc, target_pc, sp); 204 PushShadowStack(t, pc, target_pc, sp); 205 } 206 207 static void On_DirectCall(uintptr_t pc, uintptr_t target_pc, uintptr_t sp) { 208 On_AnyCall(pc, target_pc, sp, true); 209 } 210 211 static void On_IndirectCall(uintptr_t pc, uintptr_t target_pc, uintptr_t sp) { 212 On_AnyCall(pc, target_pc, sp, false); 213 } 214 215 static void On_TraceEnter(uintptr_t pc, uintptr_t sp) { 216 void *drcontext = dr_get_current_drcontext(); 217 DrThread &t = GetCurrentThread(drcontext); 218 // dr_fprintf(STDOUT, "T%d TRACE:\n%p\n%p\n", t.tid, pc, sp); 219 UpdateShadowStack(t, sp); 220 } 221 222 //--------------- instrumentation ----------------- {{{1 223 opnd_t opnd_create_base_disp_from_dst(opnd_t dst) { 224 return opnd_create_base_disp(opnd_get_base(dst), 225 opnd_get_index(dst), 226 opnd_get_scale(dst), 227 opnd_get_disp(dst), 228 OPSZ_lea); 229 } 230 231 static void InstrumentOneMop(void* drcontext, instrlist_t *bb, 232 instr_t *instr, opnd_t opnd, bool is_w) { 233 // opnd_disassemble(drcontext, opnd, 1); 234 // dr_printf(" -- (%s opnd)\n", is_w ? "write" : "read"); 235 void *callback = (void*)(is_w ? On_Write : On_Read); 236 int size = opnd_size_in_bytes(opnd_get_size(opnd)); 237 238 instr_t *tmp_instr = NULL; 239 reg_id_t reg = REG_XAX; 240 241 /* save %xax */ 242 dr_save_reg(drcontext, bb, instr, reg, SPILL_SLOT_2); 243 244 if (opnd_is_base_disp(opnd)) { 245 /* lea opnd => %xax */ 246 opnd_set_size(&opnd, OPSZ_lea); 247 tmp_instr = INSTR_CREATE_lea(drcontext, 248 opnd_create_reg(reg), 249 opnd); 250 } else if( 251 #ifdef X86_64 252 opnd_is_rel_addr(opnd) || 253 #endif 254 opnd_is_abs_addr(opnd)) { 255 tmp_instr = INSTR_CREATE_mov_imm(drcontext, 256 opnd_create_reg(reg), 257 OPND_CREATE_INTPTR(opnd_get_addr(opnd))); 258 } 259 if (tmp_instr) { 260 // CHECK(tmp_instr); 261 instrlist_meta_preinsert(bb, instr, tmp_instr); 262 263 /* clean call */ 264 dr_insert_clean_call(drcontext, bb, instr, callback, false, 265 3, 266 OPND_CREATE_INTPTR(instr_get_app_pc(instr)), 267 OPND_CREATE_INT32(size), 268 opnd_create_reg(reg)); 269 /* restore %xax */ 270 dr_restore_reg(drcontext, bb, instr, REG_XAX, SPILL_SLOT_2); 271 } else { 272 dr_printf("%s ????????????????????\n", __FUNCTION__); 273 } 274 } 275 276 static void InstrumentMopInstruction(void *drcontext, 277 instrlist_t *bb, instr_t *instr) { 278 // reads: 279 for (int a = 0; a < instr_num_srcs(instr); a++) { 280 opnd_t curop = instr_get_src(instr, a); 281 if (opnd_is_memory_reference(curop)) { 282 InstrumentOneMop(drcontext, bb, instr, curop, false); 283 } 284 } 285 // writes: 286 for (int a = 0; a < instr_num_dsts(instr); a++) { 287 opnd_t curop = instr_get_dst(instr, a); 288 if (opnd_is_memory_reference(curop)) { 289 InstrumentOneMop(drcontext, bb, instr, curop, true); 290 } 291 } 292 //dr_printf("reads: %d writes: %d\n", n_reads, n_writes); 293 } 294 295 static void InstrumentInstruction(void *drcontext, instrlist_t *bb, 296 instr_t *instr) { 297 // instr_disassemble(drcontext, instr, 1); 298 // dr_printf(" -- \n"); 299 if (instr_is_call_direct(instr)) { 300 dr_insert_call_instrumentation(drcontext, bb, instr, 301 (app_pc)On_DirectCall); 302 } else if (instr_is_call_indirect(instr)) { 303 dr_insert_mbr_instrumentation(drcontext, bb, instr, 304 (app_pc)On_IndirectCall, SPILL_SLOT_1); 305 306 } else if (instr_reads_memory(instr) || instr_writes_memory(instr)) { 307 InstrumentMopInstruction(drcontext, bb, instr); 308 } 309 } 310 311 static dr_emit_flags_t OnEvent_Trace(void *drcontext, void *tag, 312 instrlist_t *trace, bool translating) { 313 instr_t *first_instr = NULL; 314 for (instr_t *instr = instrlist_first(trace); instr != NULL; 315 instr = instr_get_next(instr)) { 316 if (instr_get_app_pc(instr)) { 317 first_instr = instr; 318 break; 319 } 320 } 321 if (first_instr) { 322 // instr_disassemble(drcontext, first_instr, 1); 323 // dr_printf(" -- in_trace %p\n", instr_get_app_pc(first_instr)); 324 dr_insert_clean_call(drcontext, trace, first_instr, 325 (void*)On_TraceEnter, false, 326 2, 327 OPND_CREATE_INTPTR(instr_get_app_pc(first_instr)), 328 opnd_create_reg(REG_XSP) 329 ); 330 } 331 return DR_EMIT_DEFAULT; 332 } 333 334 int replace_foo(int i, int j, int k) { 335 dr_printf(" dy 'foo_replace'(%i, %i, %i)\n", i, j, k); 336 return 1; 337 } 338 339 typedef unordered_map<intptr_t, void*> FunctionsReplaceMap; 340 static FunctionsReplaceMap *fun_replace_map; 341 342 namespace wrap { 343 344 int (*orig_foo)(int,int,int) = NULL; 345 int in_wrapper = 0; // TODO: Make it thread-local 346 347 static int wrapped_foo(int i, int j, int k) { 348 in_wrapper = 1; 349 350 dr_printf(" dy 'foo_wrap'(%i, %i, %i)\n", i, j, k); 351 dr_printf("orig_foo = %p\n", orig_foo); 352 int ret = 13; 353 if (orig_foo != NULL) 354 ret = orig_foo(i, j, k) + 4200; 355 else 356 dr_printf("ERROR! orig_foo is not set!\n");/**/ 357 358 in_wrapper = 0; 359 return ret; 360 } 361 362 int is_in_wrapper(int arg) { 363 // TODO: this may not work well with recursive functions 364 return in_wrapper; 365 } 366 } 367 368 void print_bb(void* drcontext, instrlist_t *bb, const char * desc) { 369 dr_printf("==================\n"); 370 dr_printf("%s:\n", desc); 371 for (instr_t *i = instrlist_first(bb); i != NULL; i = instr_get_next(i)) { 372 instr_disassemble(drcontext, i, 1); 373 dr_printf("\n"); 374 } 375 dr_printf("==================\n"); 376 } 377 378 static dr_emit_flags_t OnEvent_BB(void* drcontext, void *tag, instrlist_t *bb, 379 bool for_trace, bool translating) { 380 instr_t *first_instr = instrlist_first(bb); 381 app_pc pc = instr_get_app_pc(first_instr); 382 string symbol_name = "UNKNOWN"; 383 if (sym_tab->find((intptr_t)pc) != sym_tab->end()) { 384 symbol_name = (*sym_tab)[(intptr_t)pc]; 385 //dr_printf("Symbol = %s\n", symbol_name.c_str()); 386 } 387 388 if (fun_replace_map->count((intptr_t)pc) > 0) { 389 // Replace client function with the function supplied by the tool. 390 // The logic is inspired by drmemory/replace.c 391 app_pc target_fun = (app_pc)(*fun_replace_map)[(intptr_t)pc]; 392 const module_data_t *info = dr_lookup_module(pc); 393 dr_printf("REDIR: %s (from %s) redirected to %p\n", 394 symbol_name.c_str(), info->full_path, target_fun); 395 396 instrlist_clear(drcontext, bb); 397 instrlist_append(bb, INSTR_XL8(INSTR_CREATE_jmp(drcontext, opnd_create_pc(target_fun)), pc)); 398 } else { 399 if (StringMatch("*foo_to_wrap*", symbol_name)) { 400 const module_data_t *info = dr_lookup_module(pc); 401 dr_printf(" 'foo_to_wrap' entry point: bb %p, %s / %s\n", pc, dr_module_preferred_name(info), info->full_path); 402 wrap::orig_foo = (int (*)(int,int,int))(void*)pc; 403 404 //print_bb(drcontext, bb, "BEFORE"); 405 // TODO: Use something more optimized than clean_call 406 dr_insert_clean_call(drcontext, bb, first_instr, (void*)wrap::is_in_wrapper, 407 false, 1, OPND_CREATE_INTPTR(pc)); 408 instr_t *opr_instr = INSTR_CREATE_test(drcontext, opnd_create_reg(REG_XAX), 409 opnd_create_reg(REG_XAX)); 410 instr_t *jne_instr = INSTR_CREATE_jcc(drcontext, OP_jz, 411 opnd_create_pc((app_pc)wrap::wrapped_foo)); 412 instrlist_meta_preinsert(bb, first_instr, opr_instr); 413 instrlist_meta_preinsert(bb, first_instr, jne_instr); 414 415 //print_bb(drcontext, bb, "AFTER"); 416 } 417 418 instr_t *instr, *next_instr; 419 for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) { 420 next_instr = instr_get_next(instr); 421 if (instr_get_app_pc(instr)) // don't instrument non-app code 422 InstrumentInstruction(drcontext, bb, instr); 423 } 424 425 426 OnEvent_Trace(drcontext, tag, bb, translating); 427 } 428 429 return DR_EMIT_DEFAULT; 430 } 431 432 void ReadSymbolsTableFromFile(const char *filename) { 433 file_t f = dr_open_file(filename, DR_FILE_READ); 434 CHECK(f != INVALID_FILE); 435 436 const int BUFF_SIZE = 1 << 16; // should be enough for testing 437 char buff[BUFF_SIZE]; 438 dr_read_file(f, buff, BUFF_SIZE); 439 char *cur_line = buff; 440 while (*cur_line) { 441 char *next_line = strstr(cur_line, "\n"); 442 if (next_line != NULL) 443 *next_line = 0; 444 char fun_name[1024]; 445 char dummy; 446 void* pc; 447 sscanf(cur_line, "%p %c %s", &pc, &dummy, fun_name); 448 //dr_printf("%s => %p\n", fun_name, pc); 449 (*sym_tab)[(intptr_t)pc] = fun_name; 450 451 if (next_line == NULL) break; 452 cur_line = next_line + 1; 453 } 454 455 } 456 457 void ReplaceFunc3(void *img, void *rtn, string filter, void *fun_ptr) { 458 for (SymbolsTable::iterator i = sym_tab->begin(); i != sym_tab->end(); i++) { 459 if (StringMatch(filter, i->second)) 460 (*fun_replace_map)[(intptr_t)i->first] = fun_ptr; 461 } 462 } 463 464 //--------------- dr_init ----------------- {{{1 465 DR_EXPORT void dr_init(client_id_t id) { 466 sym_tab = new SymbolsTable; 467 468 // HACK doesn't work if multiple options are passed. 469 const char *opstr = dr_get_options(id); 470 dr_printf("Options: %s\n", opstr); 471 const char *fname = strstr(opstr, "--symbols="); 472 if (fname) { 473 ReadSymbolsTableFromFile(fname + 10); 474 } 475 476 // Register events. 477 dr_register_exit_event(OnEvent_Exit); 478 dr_register_bb_event(OnEvent_BB); 479 dr_register_trace_event(OnEvent_Trace); 480 dr_register_thread_init_event(OnEvent_ThreadInit); 481 dr_register_thread_exit_event(OnEvent_ThreadExit); 482 dr_register_module_load_event(OnEvent_ModuleLoaded); 483 g_lock = dr_mutex_create(); 484 485 fun_replace_map = new FunctionsReplaceMap(); 486 void *img = NULL, *rtn = NULL; 487 #define AFUNPTR void* 488 ReplaceFunc3(img, rtn, "memchr", (AFUNPTR)Replace_memchr); 489 ReplaceFunc3(img, rtn, "strchr", (AFUNPTR)Replace_strchr); 490 ReplaceFunc3(img, rtn, "index", (AFUNPTR)Replace_strchr); 491 ReplaceFunc3(img, rtn, "strrchr", (AFUNPTR)Replace_strrchr); 492 ReplaceFunc3(img, rtn, "rindex", (AFUNPTR)Replace_strrchr); 493 ReplaceFunc3(img, rtn, "strlen", (AFUNPTR)Replace_strlen); 494 ReplaceFunc3(img, rtn, "strcmp", (AFUNPTR)Replace_strcmp); 495 ReplaceFunc3(img, rtn, "memcpy", (AFUNPTR)Replace_memcpy); 496 ReplaceFunc3(img, rtn, "strcpy", (AFUNPTR)Replace_strcpy); 497 ReplaceFunc3(img, rtn, "*foo_to_replace*", (AFUNPTR)replace_foo); 498 } 499 // end. {{{1 500 // vim:shiftwidth=2:softtabstop=2:expandtab 501