// Tries to find a static array within structVar whose address is within // range of targetAddr. The struct's base addr is structVarBaseAddr. // The return value is the static array variable. // Remember to recurse on non-pointer struct variables within structVar // and repeat this same process because they themselves might contain // static arrays // *baseAddr = base address of the array variable // Pre: VAR_IS_BASE_STRUCT(structVar) static VariableEntry* searchForArrayWithinStruct(VariableEntry* structVar, Addr structVarBaseAddr, Addr targetAddr, Addr* baseAddr) { VarNode* v = 0; tl_assert(structVar->varType->aggType); FJALAR_DPRINTF("searchForArrayWithinStruct: %s, structVarBaseAddr: %p, targetAddr: %p, baseAddr: %p\n", structVar->name, (void*)structVarBaseAddr, (void*)targetAddr, (void*)baseAddr); // FJALAR_DPRINTF("aggType: %p, memberVarList: %p\n", // structVar->varType->aggType, // (structVar->varType->aggType ? structVar->varType->aggType->memberVarList : 0)); if (structVar->varType->aggType->memberVarList) { for (v = structVar->varType->aggType->memberVarList->first; v != 0; v = v->next) { VariableEntry* potentialVar; Addr potentialVarBaseAddr; potentialVar = v->var; tl_assert(IS_MEMBER_VAR(potentialVar)); FJALAR_DPRINTF("examining: %s, offset: %ld\n", potentialVar->name, potentialVar->memberVar->data_member_location); // FJALAR_DPRINTF("staticArr: %p, ptrLevels: %d, varType: %d\n", // potentialVar->staticArr, potentialVar->ptrLevels, // (potentialVar->varType ? potentialVar->varType->decType : 0)); potentialVarBaseAddr = structVarBaseAddr + potentialVar->memberVar->data_member_location; if (IS_STATIC_ARRAY_VAR(potentialVar) && (potentialVarBaseAddr <= targetAddr) && (targetAddr < (potentialVarBaseAddr + (potentialVar->staticArr->upperBounds[0] * getBytesBetweenElts(potentialVar))))) { *baseAddr = potentialVarBaseAddr; FJALAR_DPRINTF("Wins: %s\n", potentialVar->name); return potentialVar; } // Recursive step (be careful to avoid infinite recursion) else if VAR_IS_BASE_STRUCT(potentialVar) { VariableEntry* targetVar = searchForArrayWithinStruct(potentialVar, potentialVarBaseAddr, targetAddr, baseAddr); if (targetVar) { FJALAR_DPRINTF("wins: %s\n", potentialVar->name); return targetVar; } } } } *baseAddr = 0; return 0; }
// Takes a pointer to a variable of size typeSize starting at startAddr // and probes ahead to see how many contiguous blocks of memory are allocated // (using memcheck check_writable()) for that variable starting at startAddr. // This is used to determine whether a pointer points to one variable // (return 1) or whether it points to an array (return > 1). // We can use this function to determine the array size at runtime // so that we can properly output the variable as either a single // variable or an array // NOTE! If you pass a pointer to the MIDDLE of an array as startAddr, // this function will return the number of entries in the array AFTER // the pointer since it only probes AHEAD and NOT BEHIND! // // This is very flaky!!! It only works properly for heap allocated // arrays since the stack and global space contain lots of squished-together // contiguous variables // // Now we do a two-pass approach which first goes FORWARD until it // hits a set of bytes of size typeSize whose A-bits are all unset and // then BACKWARDS until it hits the first set of bytes of size // typeSize with at least ONE byte whose V-bit is SET. This avoids // printing out large chunks of garbage values when most elements of // an array are uninitialized. For example, this function will return // 10 for an int array allocated to hold 1000 elements but only with // the first 10 elements initialized. int probeAheadDiscoverHeapArraySize(Addr startAddr, UInt typeSize) { int arraySize = 0; /*tl_assert(typeSize > 0);*/ if (typeSize == 0) return 0; FJALAR_DPRINTF ( "typeSize: 0x%x\n", typeSize); while (mc_check_writable( startAddr, typeSize, 0)) { if (arraySize % 1000 == 0) FJALAR_DPRINTF ( "Made it to %d elements at 0x%x\n", arraySize, (unsigned int)startAddr); /* Cut off the search if we can already see it's really big: no need to look further than we're going to print. */ // RUDD TEMP /* if (fjalar_array_length_limit != -1 && */ /* arraySize > fjalar_array_length_limit) */ /* break; */ arraySize++; startAddr+=typeSize; } startAddr -= typeSize; // Now do a SECOND pass and probe BACKWARDS until we reach the // first set of bytes with at least one byte whose V-bit is SET while ((arraySize > 0) && // If at least ONE byte within the element of size typeSize // is initialized, then consider the entire element to be // initialized. This is done because sometimes only certain // members of a struct are initialized, and if we perform the // more stringent check for whether ALL members are // initialized, then we will falsely mark // partially-initialized structs as uninitialized and lose // information. For instance, consider struct point{int x; // int y;} - Let's say you had struct point foo[10] and // initialized only the 'x' member var. in every element of // foo (foo[0].x, foo[1].x, etc...) but left the 'y' member // var uninitialized. Every element of foo has typeSize = 2 // * sizeof(int) = 8, but only the first 4 bytes are // initialized ('x') while the last 4 are uninitialized // ('y'). This function should return 10 for the size of // foo, so it must mark each element as initialized when at // least ONE byte is initialized (in this case, a byte within // 'x'). !mc_are_some_bytes_initialized(startAddr, typeSize)) { arraySize--; startAddr-=typeSize; } return arraySize; }
// Return the number of bytes between elements of this variable // if it were used as an array int getBytesBetweenElts(VariableEntry* var) { tl_assert(var); if (var->ptrLevels > 1) { FJALAR_DPRINTF("getBytesBetweenElts returning sizeof(void*) (%zu)\n", sizeof(void*)); return sizeof(void*); } else { FJALAR_DPRINTF("getBytesBetweenElts returning %d\n", var->varType->byteSize); return var->varType->byteSize; } }
// If the first basic block of a function ends before entryPC, use // the last instruction of that basic block as our entrypoint. Otherwise // use entryPC static void find_entry_pt(IRSB* bb_orig, FunctionEntry *f) { int i; Addr entry_pt = 0; if(gencontains(funcs_handled, f)) return; if (dyncomp_delayed_trace) { dyncomp_print_trace_info = True; dyncomp_delayed_trace = False; } if (dyncomp_delayed_print_IR) { fjalar_print_IR = True; dyncomp_delayed_print_IR = False; } FJALAR_DPRINTF("[find_entry_pt] Searching %s for entry address %x\n", f->fjalar_name, (UInt)f->entryPC); for(i=0 ; i < bb_orig->stmts_used; i++) { IRStmt *st = bb_orig->stmts[i]; if(st->tag == Ist_IMark) { FJALAR_DPRINTF("\tEncountered IMark for address %x\n", (UInt)st->Ist.IMark.addr); if(st->Ist.IMark.addr <= f->entryPC) { entry_pt = st->Ist.IMark.addr; } } } tl_assert( entry_pt ); FJALAR_DPRINTF("\t%x chosen for entry\n", (UInt)entry_pt); genputtable(funcs_handled, (void *)f, (void *)1); genputtable(FunctionTable_by_endOfBb, (void *)entry_pt, (void *)f); }
// Return a pointer to a FunctionExecutionState which contains the address // specified by "a" in its stack frame // Assumes: The stack grows DOWNWARD on all supported platforms so this // returns the function entry with the smallest FP that is HIGHER // than "a" and a lowestSP that is LOWER than "a" // Returns 0 if no function found static FunctionExecutionState* returnFunctionExecutionStateWithAddress(Addr a) { Int i; ThreadId tid = VG_(get_running_tid)(); FunctionExecutionState* cur_fn = 0; FunctionExecutionState* next_fn = 0; FJALAR_DPRINTF("Looking for function corresponding " "to stack variable 0x%p\n", (void *)a); // Traverse the function stack from the function with // the highest ESP to the one with the lowest ESP // but DON'T LOOK at the function that's the most // recent one on the stack yet - hence 0 <= i <= (fn_stack_first_free_index - 2) for (i = 0; i <= fn_stack_first_free_index[tid] - 2; i++) { cur_fn = &FunctionExecutionStateStack[tid][i]; next_fn = &FunctionExecutionStateStack[tid][i + 1]; if (!cur_fn || !next_fn) { printf( "Error in returnFunctionExecutionStateWithAddress()"); my_abort(); } FJALAR_DPRINTF("cur_fn->FP: %p\n", (void *)cur_fn->FP); FJALAR_DPRINTF("next_fn->FP: %p\n", (void *)next_fn->FP); // If it is not the most recent function pushed on the stack, // then the stack frame of this function lies in between // the EBP of that function and the function immediately // following it on the stack if ((cur_fn->FP >= a) && (next_fn->FP <= a)) { FJALAR_DPRINTF("Returning functionEntry: %p\n", cur_fn); return cur_fn; } } // If a function hasn't been found yet, now // look at the most recent function on the stack: // If it is the most recent function on the stack, // then the stack frame can only be approximated to lie // in between its FP and lowestSP // (this isn't exactly accurate because there are issues // with lowestSP, but at least it'll give us some info.) cur_fn = fnStackTop(tid); if ((cur_fn->FP >= a) && (cur_fn->lowestSP <= a)) { FJALAR_DPRINTF("Returning functionEntry: %zx\n", (ptrdiff_t)cur_fn); return cur_fn; } FJALAR_DPRINTF(" EXIT FAILURE returnFunctionExecutionStateWithAddress\n"); return 0; }
// Call this AFTER initializeAllFjalarData() so that all relevant data // structures are already initialized. // Try to open a .disambig file for reading, but if it doesn't exist, // create a new file by writing to it. // Pre: fjalar_disambig_filename is non-null void handleDisambigFile() { tl_assert(fjalar_disambig_filename); if ((disambig_fp = fopen(fjalar_disambig_filename, "r"))) { FJALAR_DPRINTF("\n\nREADING %s\n", fjalar_disambig_filename); disambig_writing = False; VG_(printf)( "\nBegin processing disambiguation file \"%s\" ...\n", fjalar_disambig_filename); processDisambigFile(); VG_(printf)( "Done processing disambiguation file \"%s\"\n", fjalar_disambig_filename); } else if ((disambig_fp = fopen(fjalar_disambig_filename, "wx"))) { FJALAR_DPRINTF("\n\nWRITING %s\n", fjalar_disambig_filename); disambig_writing = True; // If we are writing a .disambig file, then we always want to // visit all the struct variables so that we can generate // .disambig entries for them: fjalar_output_struct_vars = True; // If fjalar_smart_disambig is on, then we need to wait until the // END of program execution before printing out the .disambig // information (see fjalar_finish()): if (!fjalar_smart_disambig) { generateDisambigFile(); VG_(printf)("\nDone generating .disambig file %s\n", fjalar_disambig_filename); fclose(disambig_fp); disambig_fp = 0; VG_(exit)(0); } } }
void exit_function(FunctionEntry* f) { ThreadId currentTID = VG_(get_running_tid)(); FunctionExecutionState* top = fnStackTop(currentTID); extern FunctionExecutionState* curFunctionExecutionStatePtr; int i; ULong FPUshadow; double fpuReturnVal; Addr xAX, xDX, xAXshadow, xDXshadow; Bool foundFunc; FJALAR_DPRINTF("Exit function: %s\n", f->fjalar_name); top->func->guestStackStart = top->lowSP - VG_STACK_REDZONE_SZB; top->func->guestStackEnd = top->func->guestStackStart + top->virtualStackByteSize; top->func->lowestVirtSP = (Addr)top->virtualStack; top->func->FP = top->FP; top->func->lowestSP = top->func->guestStackStart; FJALAR_DPRINTF("\tState of Guest Stack [%p - %p] \n", (void *)top->lowestSP, (void *)(top->lowestSP + top->virtualStackByteSize)); // Ok, in Valgrind 2.X, we needed to directly code some assembly to // grab the top of the floating-point stack, but Valgrind 3.0 // provides a virtual FPU stack, so we can just grab that. Plus, we // now have shadow V-bits for the FPU stack. #if defined(VGA_amd64) // XMM registers are represented as a 128-bit integer // (Actually an array of 4 32-bit integers), so reinterpret // cast it as a double. FPUshadow = (VG_(get_shadow_XMM_N)(currentTID, 0))[0]; fpuReturnVal = *(double *)VG_(get_XMM_N)(currentTID, 0); //RUDD DEBUG /* for(i=0 ; i < 4; i++) { */ /* printf("Testing %d\n", test[i]); */ /* printf("shadow: %x\n", testing[i]); */ /* } */ #else fpuReturnVal = VG_(get_FPU_stack_top)(currentTID); FPUshadow = VG_(get_shadow_FPU_stack_top)(currentTID); #endif //RUDD DEBUG /* printf("%.16g\n", fpuReturnVal); */ /* printf("shadow: %x\n", FPUshadow); */ // Get the value at the simulated %EAX (integer and pointer return // values are stored here upon function exit) xAX = VG_(get_xAX)(currentTID); // Get the value of the simulated %EDX (the high 32-bits of the long // long int return value is stored here upon function exit) xDX = VG_(get_xDX)(currentTID); // 64 bits // Use SHADOW values of Valgrind simulated registers to get V-bits xAXshadow = VG_(get_shadow_xAX)(currentTID); xDXshadow = VG_(get_shadow_xDX)(currentTID); FJALAR_DPRINTF("Value of eax: %d, edx: %d\n",(int)xAX, (int)xDX); FJALAR_DPRINTF("Exit function: %s\n", f->fjalar_name); // Only do something if top->func matches func if (!top->func) { printf("More exit_function()s than entry_function()s!\n"); // RUDD EXCEPTION VG_(get_and_pp_StackTrace) (currentTID, 15); return; } else if (!(top->func->fjalar_name) || (top->func != f)) { // There are a couple reasons why the function at the top // of the Function Execution State Stack would not be the // function we expected. All of them are related to a non- // local exit of the function. Situations where this might // come up include: // (1) C++ Exceptions // (2) setjmp/longjmp // For Fjalar to resume normal operation, we need to pop // off functions off the Function Execution State Stack // (these would be all the functions that had non-local // exits) until we encounter the Function Execution State // Stack corresponding to our function. printf("MISMATCHED on exit_function! f: %s != %s\nDetectedEntryIP: %p - AssumedEntryIP: %p\nDetctedExitIP: %p - AssumedExitIp: %p\n", f->fjalar_name, top->func->fjalar_name, (void *)top->func->entryPC, (void *)f->entryPC, (void *)top->func->endPC, (void *)f->endPC); // This is probably being overconservative. However let's revert // to Fjalar's old behavior (do nothing) if we can't find an // instance of our function in the function stack. for(i = fn_stack_first_free_index[currentTID] - 1; i >= 0; i-- ) { FunctionExecutionState* curFuncExecPtr = &FunctionExecutionStateStack[currentTID][i]; if(curFuncExecPtr->func == f) { foundFunc = True; break; } } if(!foundFunc) { VG_(get_and_pp_StackTrace) (currentTID, 15); return; } while(top->func) { top = fnStackTop(currentTID); if(top->func == f) { break; } fnStackPop(currentTID); } tl_assert(top->func == f); } top->xAX = xAX; top->xDX = xDX; top->FPU = fpuReturnVal; // Very important! Set the A and V bits of the appropriate // FunctionExecutionState object and the tags from the (x86) guest // state as well: for (i = 0; i < sizeof(Addr); i++) { set_abit_and_vbyte((Addr)(&top->xAX) + (Addr)i, VGM_BIT_VALID, (xAXshadow & 0xff) << (i * 8)); set_abit_and_vbyte((Addr)(&top->xDX) + (Addr)i, VGM_BIT_VALID, (xDXshadow & 0xff) << (i * 8)); set_abit_and_vbyte((Addr)(&top->FPU) + (Addr)i, VGM_BIT_VALID, (FPUshadow & 0xff) << (i * 8)); } for (i = 0; i < 8; i++) { set_abit_and_vbyte((Addr)(&top->FPU) + (Addr)i, VGM_BIT_VALID, (FPUshadow & 0xff) << (i * 8)); } curFunctionExecutionStatePtr = top; top->func->nonce = top->invocation_nonce; fjalar_tool_handle_function_exit(top); // Destroy the memory allocated by virtualStack // AFTER the tool has handled the exit if (top->virtualStack) { /* We were previously using the V bits associated with the area to store guest V bits, but Memcheck doesn't normally expect VG_(malloc)'ed memory to be client accessible, so we have to make it inaccessible again before allowing Valgrind's malloc to use it, lest assertions fail later. */ mc_make_noaccess((Addr)top->virtualStack, top->virtualStackByteSize); VG_(free)(top->virtualStack); } // Pop at the VERY end after the tool is done handling the exit. // This is subtle but important - this must be done AFTER the tool // runs all of it's function exit code, as functions in fjalar_traversal // and fjalar_runtime may make use of the function stack. If we fail to // pop the function, however, the stack will be left in an inconsistant // state and the "!top->func == f" check will fail causing no more // program points to be printed. fnStackPop(currentTID); }
void enter_function(FunctionEntry* f) { FunctionExecutionState* newEntry; extern FunctionExecutionState* curFunctionExecutionStatePtr; ThreadId tid = VG_(get_running_tid)(); Addr stack_ptr= VG_(get_SP)(tid); Addr frame_ptr = 0; /* E.g., %ebp */ int local_stack, size; FJALAR_DPRINTF("[enter_function] startPC is: %x, entryPC is: %x, cu_base: %p\n", (UInt)f->startPC, (UInt)f->entryPC,(void *)f->cuBase); FJALAR_DPRINTF("Value of edi: %lx, esi: %lx, edx: %lx, ecx: %lx\n", (long)VG_(get_xDI)(tid), (long)VG_(get_xSI)(tid), (long)VG_(get_xDX)(tid), (long)VG_(get_xCX)(tid)); // Determine the frame pointer for this function using DWARF // location lists. This is a "virtual frame pointer" in that it is // used by the DWARF debugging information in providing the address // of formal parameters and local variables, but it may or may not // correspond to an actual frame pointer in the architecture. For // example: This will not always return %xbp on x86{-64} platforms // and *SHOULD*(untested) work with the -fomit-frame-pointer flag in GCC // // It usually points just above the function return address. The // .debug_loc info tells how to find (calculate) the frame base // at any point in the program. (markro) if(f->locList) { Addr eip = f->entryPC; location_list *ll; eip = eip - f->cuBase; FJALAR_DPRINTF("\tCurrent EIP is: %x\n", (UInt)eip); FJALAR_DPRINTF("\tLocation list based function(offset from base: %x). offset is %lu\n",(UInt)eip, f->locListOffset); if (gencontains(loc_list_map, (void *)f->locListOffset)) { ll = gengettable(loc_list_map, (void *)f->locListOffset); // (comment added 2009) // HACK. g++ and GCC handle location lists differently. GCC puts lists offsets // relative to the compilation unit, g++ uses the actual address. I'm going to // compare the location list ranges both to the cu_base offset, as well as // the function's entry point. This might break if there's every a case // where the compilation unit offset is a valid address in the program while(ll && !(((ll->begin <= eip) && (ll->end >= eip)) || ((ll->begin <= f->entryPC) && (ll->end >= f->entryPC)))) { FJALAR_DPRINTF("\tExamining loc list entry: %x - %x - %x\n", (UInt)ll->offset, (UInt)ll->begin, (UInt)ll->end); ll = ll->next; } if(ll) { FJALAR_DPRINTF("\tFound location list entry, finding location corresponding to dwarf #: %d with offset: %lld\n", ll->atom, ll->atom_offset); // (comment added 2013) // It turns out it might not be just the contents of a register. Some // 32bit x86 code does some tricky stack alignment and has to save a // pointer to the orginal stack frame. This means we get passed a // DW_OP_deref instead of a DW_OP_breg. The tricky bit is we don't // want to go back to that address because it probably won't be equal // to the local frame pointer due to the stack alignment. So the HACK // is to just assume the frame pointer is at EBP+8 like normal. (markro) if (ll->atom == DW_OP_deref) { ll->atom = DW_OP_breg5; ll->atom_offset = 8; } if(get_reg[ll->atom - DW_OP_breg0]) { frame_ptr = (*get_reg[ll->atom - DW_OP_breg0])(tid) + ll->atom_offset; } } } } // This is the old code to determine the frame. Fallback to it if we don't // have a frame_base from the location_list path. This should keep GCC 3 working // fine. if(frame_ptr == 0) { if (f != primed_function) { printf("No location list or frame pointer giving up(Mangled name: %s)\n", f->mangled_name); return; } primed_function = 0; if (f->entryPC != f->startPC) { /* Prolog has run, so just use the real %ebp */ frame_ptr = VG_(get_FP)(VG_(get_running_tid)()); } else { FJALAR_DPRINTF("Faking prolog\n"); /* Don't know about prolog, so fake its effects, given we know that ESP hasn't yet been modified: */ // Looks like we never get here for amd64 as -4 is clearly wrong. (10/26/2015) frame_ptr = stack_ptr - 4; } } FJALAR_DPRINTF("\tEnter function: %s - StartPC: %p, EntryPC: %p, frame_ptr: %p\n", f->fjalar_name, (void *)f->startPC, (void *)f->entryPC, (void *)frame_ptr); newEntry = fnStackPush(tid); newEntry->func = f; newEntry->func->FP = frame_ptr; newEntry->func->lowestSP = stack_ptr; newEntry->FP = frame_ptr; newEntry->lowSP = stack_ptr; newEntry->lowestSP = stack_ptr; newEntry->xAX = 0; newEntry->xDX = 0; newEntry->FPU = 0; newEntry->invocation_nonce = cur_nonce++; newEntry->func->nonce = newEntry->invocation_nonce; // FJALAR VIRTUAL STACK // Fjalar maintains a virtual stack for invocation a function. This // allows Fjalar to provide tools with unaltered values of formal // parameters at both function entry and exit, regardless of whether // or not the compiler chooses to use the original formal parameter // locations as storage for local values. // Initialize virtual stack and copy parts of the Valgrind stack // into that virtual stack local_stack = frame_ptr - stack_ptr + VG_STACK_REDZONE_SZB; /* in our frame */ tl_assert(local_stack >= 0); FJALAR_DPRINTF("frame_ptr: %p, stack_ptr: %p, VG_STACK_REDZONE: %d\n", (void *)frame_ptr, (void *)stack_ptr, VG_STACK_REDZONE_SZB); // The virtual stack consists of: // (1) local_stack: the entirety of the function's local stack (the // memory between the frame pointer and the stack pointer (including the extra // redzone) // (2) The return pointer, which is sizeof(Addr) bytes // (3) The saved base pointer, which is sizeof(Addr) bytes // (4) All formal parameters passed on the stack, which is // f->formalParamStackByteSize bytes // Let's be conservative in how much we copy over to the Virtual stack. Due to the // stack alignment operations in main, we may need as much as 16 bytes over the above. size = local_stack + f->formalParamStackByteSize + sizeof(Addr)*2 + 32;/* plus stuff in caller's*/ FJALAR_DPRINTF("local_stack: %p, arg_size: %x\n", (void *)(frame_ptr - f->formalParamLowerStackByteSize), f->formalParamLowerStackByteSize); int delta = stack_ptr - (frame_ptr - f->formalParamLowerStackByteSize); if (delta < 0 ) delta = 0; tl_assert(size >= 0); if (size != 0) { newEntry->virtualStack = VG_(calloc)("fjalar_main.c: enter_func", size, sizeof(char)); newEntry->virtualStackByteSize = size; newEntry->virtualStackFPOffset = local_stack; clear_all_tags_in_range(stack_ptr - VG_STACK_REDZONE_SZB, VG_STACK_REDZONE_SZB - delta); VG_(memcpy)(newEntry->virtualStack, (char*)stack_ptr - VG_STACK_REDZONE_SZB, size); // VERY IMPORTANT!!! Copy all the A & V bits over the real stack to // virtualStack!!! (As a consequence, this copies over the tags // as well - look in mc_main.c). Note that the way do this means // that the copy is now guest-accessible, if they guessed the // VG_(calloc)ed address, which is a bit weird. It would be more // elegant to copy the metadata to an inaccessible place, but that // would be more work. FJALAR_DPRINTF("Copying over stack [%p] -> [%p] %d bytes\n",(void *)(stack_ptr - VG_STACK_REDZONE_SZB), (void *)newEntry->virtualStack, size); mc_copy_address_range_state(stack_ptr - VG_STACK_REDZONE_SZB, (Addr)(newEntry->virtualStack), size); newEntry->func->guestStackStart = stack_ptr - VG_STACK_REDZONE_SZB; newEntry->func->guestStackEnd = newEntry->func->guestStackStart + size; newEntry->func->lowestVirtSP = (Addr)newEntry->virtualStack; } else { printf("Obtained a stack size of 0 for Function: %s. Aborting\n", f->fjalar_name); tl_assert(0); } // Do this AFTER initializing virtual stack and lowestSP curFunctionExecutionStatePtr = newEntry; fjalar_tool_handle_function_entrance(newEntry); }
// Handle a function exit statement, which contains a jump kind of // 'Ret'. It seems pretty accurate to cue off of currentAddr, a value // that is updated every time an Ist_IMark statement is translated, // which is quite often void handle_possible_exit(MCEnv* mce, IRJumpKind jk) { if (Ijk_Ret == jk) { IRDirty *di; FunctionEntry* curFuncPtr = getFunctionEntryFromAddr(currentAddr); if (curFuncPtr && // Also, if fjalar_trace_prog_pts_filename is on (we are // reading in a ppt list file), then DO NOT generate IR code // to call helper functions for functions whose names are NOT // located in prog_pts_tree. This will greatly speed up // processing because these functions are filtered out at // translation-time, not at run-time (!fjalar_trace_prog_pts_filename || prog_pts_tree_entry_found(curFuncPtr))) { FJALAR_DPRINTF("[handle_possible_exit] %s - %x\n", curFuncPtr->fjalar_name, (UInt)currentAddr); // The only argument to exit_function() is a pointer to the // FunctionEntry for the function that we are exiting di = unsafeIRDirty_0_N(1/*regparms*/, "exit_function", &exit_function, mkIRExprVec_1(IRExpr_Const(IRConst_UWord((Addr)curFuncPtr)))); // For function exit, we are interested in observing all general purpose // integer registers, FTOP, and FPREG[], so make sure that they are // updated by setting the proper annotations. di->nFxState = 11; vex_bzero(&di->fxState, sizeof(di->fxState)); di->fxState[0].fx = Ifx_Read; di->fxState[0].offset = mce->layout->offset_SP; di->fxState[0].size = mce->layout->sizeof_SP; di->fxState[1].fx = Ifx_Read; di->fxState[1].offset = mce->layout->offset_FP; di->fxState[1].size = mce->layout->sizeof_FP; di->fxState[2].fx = Ifx_Read; di->fxState[2].offset = mce->layout->offset_IP; di->fxState[2].size = mce->layout->sizeof_IP; di->fxState[3].fx = Ifx_Read; di->fxState[3].offset = mce->layout->offset_xAX; di->fxState[3].size = mce->layout->sizeof_xAX; di->fxState[4].fx = Ifx_Read; di->fxState[4].offset = mce->layout->offset_xBX; di->fxState[4].size = mce->layout->sizeof_xBX; di->fxState[5].fx = Ifx_Read; di->fxState[5].offset = mce->layout->offset_xCX; di->fxState[5].size = mce->layout->sizeof_xCX; di->fxState[6].fx = Ifx_Read; di->fxState[6].offset = mce->layout->offset_xDX; di->fxState[6].size = mce->layout->sizeof_xDX; di->fxState[7].fx = Ifx_Read; di->fxState[7].offset = mce->layout->offset_xSI; di->fxState[7].size = mce->layout->sizeof_xSI; di->fxState[8].fx = Ifx_Read; di->fxState[8].offset = mce->layout->offset_xDI; di->fxState[8].size = mce->layout->sizeof_xDI; di->fxState[9].fx = Ifx_Read; di->fxState[9].offset = offsetof(VexGuestArchState, guest_FTOP); di->fxState[9].size = sizeof(UInt); /* FTOP is 4 bytes even on x64 */ di->fxState[10].fx = Ifx_Read; di->fxState[10].offset = offsetof(VexGuestArchState, guest_FPREG); di->fxState[10].size = 8 * sizeof(ULong); stmt('V', mce, IRStmt_Dirty(di) ); } } }
// This inserts an IR Statement responsible for calling func // code before the instruction at addr is executed. This is primarily // used for inserting the call to enter_function on function entry. // It is also used for handling of 'function priming' for GCC 3 (see // comment above prime_function). The result of looking up addr in // table will be passed to func as it's only argument. This function // does nothing if it is unable to successfully look up addr in the // provided table. static void handle_possible_entry_func(MCEnv *mce, Addr64 addr, struct genhashtable *table, const char *func_name, entry_func func) { IRDirty *di; FunctionEntry *entry = gengettable(table, (void *)(Addr)addr); if(!entry) { return; } // If fjalar_trace_prog_pts_filename is on (we are using a ppt list // file), then DO NOT generate IR code to call helper functions for // functions whose name is NOT located in prog_pts_tree. It's faster // to filter them out at translation-time instead of run-time if (entry && (!fjalar_trace_prog_pts_filename || prog_pts_tree_entry_found(entry))) { UWord entry_w = (UWord)entry; di = unsafeIRDirty_0_N(1/*regparms*/, func_name, func, mkIRExprVec_1(IRExpr_Const(IRConst_UWord(entry_w)))); // For function entry, we are interested in observing the stack // and frame pointers so make sure that they're updated by setting // the proper annotations: entry->entryPC = addr; FJALAR_DPRINTF("Found a valid entry point at %x for\n", (UInt)addr); // We need all general purpose registers. di->nFxState = 9; vex_bzero(&di->fxState, sizeof(di->fxState)); di->fxState[0].fx = Ifx_Read; di->fxState[0].offset = mce->layout->offset_SP; di->fxState[0].size = mce->layout->sizeof_SP; di->fxState[1].fx = Ifx_Read; di->fxState[1].offset = mce->layout->offset_FP; di->fxState[1].size = mce->layout->sizeof_FP; di->fxState[2].fx = Ifx_Read; di->fxState[2].offset = mce->layout->offset_IP; di->fxState[2].size = mce->layout->sizeof_IP; di->fxState[3].fx = Ifx_Read; di->fxState[3].offset = mce->layout->offset_xAX; di->fxState[3].size = mce->layout->sizeof_xAX; di->fxState[4].fx = Ifx_Read; di->fxState[4].offset = mce->layout->offset_xBX; di->fxState[4].size = mce->layout->sizeof_xBX; di->fxState[5].fx = Ifx_Read; di->fxState[5].offset = mce->layout->offset_xCX; di->fxState[5].size = mce->layout->sizeof_xCX; di->fxState[6].fx = Ifx_Read; di->fxState[6].offset = mce->layout->offset_xDX; di->fxState[6].size = mce->layout->sizeof_xDX; di->fxState[7].fx = Ifx_Read; di->fxState[7].offset = mce->layout->offset_xSI; di->fxState[7].size = mce->layout->sizeof_xSI; di->fxState[8].fx = Ifx_Read; di->fxState[8].offset = mce->layout->offset_xDI; di->fxState[8].size = mce->layout->sizeof_xDI; stmt('V', mce, IRStmt_Dirty(di) ); } }
// Initialize Fjalar after command-line options are processed void fjalar_post_clo_init() { const char* DISAMBIG = ".disambig"; int DISAMBIG_LEN = VG_(strlen)(DISAMBIG); // We need to turn off some VEX IR optimizations (primarily the one which // causes separate basic blocks to be stitched together) for the purpose of // detecting entry in main. see "HANDLING FUNCTION ENTRY" in find_entry_pt() VG_(clo_vex_control).iropt_unroll_thresh = 0; VG_(clo_vex_control).guest_chase_thresh = 0; executable_filename = VG_(args_the_exename); if (fjalar_with_gdb) { int x = 0; while (!x) {} /* In GDB, say "p x=1" and then "c" to continue */ } funcs_handled= genallocatehashtable(0,(int (*)(void *,void *)) &equivalentIDs); // Handle variables set by command-line options: // Assumes that filename is first argument in client_argv FJALAR_DPRINTF("\nReading binary file \"%s\" [0x%p]\n\n", executable_filename, executable_filename); // --disambig results in the disambig filename being ${executable_filename}.disambig // (overrides --disambig-file option) if (fjalar_default_disambig) { char* disambig_filename = VG_(calloc)("fjalar_main.c: fj_po_clo_init", VG_(strlen)(executable_filename) + DISAMBIG_LEN + 1, sizeof(*disambig_filename)); VG_(strcpy)(disambig_filename, executable_filename); VG_(strcat)(disambig_filename, DISAMBIG); fjalar_disambig_filename = disambig_filename; } // There is a bug in my_libc.c that keeps printf and // putchar from intermixing properly. Using NOBUF // on stdout keeps putchar from buffering incorrectly. setNOBUF(stdout); FJALAR_DPRINTF("\n%s\n\n", fjalar_disambig_filename); // Calls into typedata.c: initialize_typedata_structures(); FJALAR_DPRINTF("Typedata structures completed\n"); // Calls into readelf.c: process_elf_binary_data(executable_filename); FJALAR_DPRINTF("Process elf binary completed\n"); // Call this BEFORE initializeAllFjalarData() so that the vars_tree // objects can be initialized for the --var-list-file option: loadAuxiliaryFileData(); // Calls into generate_fjalar_entries.c: initializeAllFjalarData(); FJALAR_DPRINTF("Fjalar data initialized\n"); if (fjalar_disambig_filename) { handleDisambigFile(); } // Call this AFTER initializeAllFjalarData() so that all of the // proper data is ready: outputAuxiliaryFilesAndExit(); FJALAR_DPRINTF("Files output\n"); // Make sure to execute this last! fjalar_tool_post_clo_init(); FJALAR_DPRINTF("Tool clo initialized\n"); }
// Prints a .disambig file entry for a given variable // This consists of 2 lines: // variable name, disambig type // e.g., // /foo <-- variable name // S <-- disambig type static TraversalResult printDisambigAction(VariableEntry* var, const HChar* varName, VariableOrigin varOrigin, UInt numDereferences, UInt layersBeforeBase, Bool overrideIsInit, DisambigOverride disambigOverride, Bool isSequence, Addr pValue, Addr pValueGuest, Addr* pValueArray, Addr* pValueArrayGuest, UInt numElts, FunctionEntry* varFuncInfo, Bool isEnter) { /* silence unused variable warnings */ (void)varOrigin; (void)numDereferences; (void)layersBeforeBase; (void)overrideIsInit; (void)disambigOverride; (void)isSequence; (void)pValue; (void)pValueArray; (void)numElts; (void)varFuncInfo; (void)isEnter; (void)pValueGuest; (void)pValueArrayGuest; FJALAR_DPRINTF(" printDisambigAction: %s\n", varName); // If this is not a variable that's worthy of being outputted to the // .disambig file, then punt: if (!shouldOutputVarToDisambig(var)) { // We do not want to traverse any further than the surface level // for .disambig: return STOP_TRAVERSAL; } // Line 1: Variable name fputs(varName, disambig_fp); fputs("\n", disambig_fp); // Line 2: Disambig type /* Default values: Base type "char" and "unsigned char": 'I' for integer Pointer to "char": 'S' for string Pointer to all other types: - 'A' for array if var->disambigMultipleElts, which means that we've observed array behavior during program's execution or if !var->pointerHasEverBeenObserved, which means that the pointer has never been observed so a conservative guess of 'A' should be the default or if var->isStructUnionMember - don't try to be smart about member variables within structs/unions - just default to "A" - 'P' for pointer if (var->pointerHasEverBeenObserved && !var->disambigMultipleElts) */ if (0 == var->ptrLevels) { if ((D_CHAR == var->varType->decType) || (D_UNSIGNED_CHAR == var->varType->decType)) { fputs("I", disambig_fp); } } // Special case for C++ 'this' parameter - always make it 'P' else if (VG_STREQ(var->name, "this")) { fputs("P", disambig_fp); } // Normal string, not pointer to string else if (IS_STRING(var) && (1 == var->ptrLevels)) { fputs("S", disambig_fp); } else if (var->ptrLevels > 0) { if (IS_MEMBER_VAR(var)) { fputs("A", disambig_fp); } else { if (var->pointerHasEverBeenObserved) { if (var->disambigMultipleElts) { fputs("A", disambig_fp); } else { fputs("P", disambig_fp); } } // default behavior for variable that was // never observed during the execution else { fputs("A", disambig_fp); } } } fputs("\n", disambig_fp); // We do not want to traverse any further than the surface level for // .disambig: return STOP_TRAVERSAL; }
// Pre: disambig_fp has been initialized and disambig_writing is True void generateDisambigFile() { FuncIterator* funcIt; TypeIterator* typeIt; FJALAR_DPRINTF("\n=> generateDisambigFile: Start Processing\n"); // Write entries for global variables: fputs(ENTRY_DELIMETER, disambig_fp); fputs("\n", disambig_fp); fputs(GLOBAL_STRING, disambig_fp); fputs("\n", disambig_fp); visitVariableGroup(GLOBAL_VAR, 0, 0, 0, 0, &printDisambigAction); FJALAR_DPRINTF("=> generateDisambigFile: Finished Globals\n\n"); fputs("\n", disambig_fp); // Write entries for function parameters and return values: funcIt = newFuncIterator(); while (hasNextFunc(funcIt)) { FunctionEntry* cur_entry = nextFunc(funcIt); tl_assert(cur_entry); // Only write .disambig entries for program points that are listed // in prog-pts-file, if we are using the --prog-pts-file option: if (!fjalar_trace_prog_pts_filename || // If fjalar_trace_prog_pts_filename is on (we are reading in // a ppt list file), then DO NOT OUTPUT entries for program // points that we are not interested in. prog_pts_tree_entry_found(cur_entry)) { fputs(ENTRY_DELIMETER, disambig_fp); fputs("\n", disambig_fp); fputs(FUNCTION_PREFIX, disambig_fp); fputs(cur_entry->fjalar_name, disambig_fp); fputs("\n", disambig_fp); // Print out all function parameter return value variable names: visitVariableGroup(FUNCTION_FORMAL_PARAM, cur_entry, 0, 0, 0, &printDisambigAction); visitVariableGroup(FUNCTION_RETURN_VAR, cur_entry, 0, 0, 0, &printDisambigAction); fputs("\n", disambig_fp); } } deleteFuncIterator(funcIt); FJALAR_DPRINTF("=> generateDisambigFile: Finished Functions\n\n"); // Write entries for every struct/class in TypesTable, with the // type's name prefixed by 'usertype.': typeIt = newTypeIterator(); while (hasNextType(typeIt)) { TypeEntry* cur_entry = nextType(typeIt); tl_assert(cur_entry && cur_entry->typeName); fputs(ENTRY_DELIMETER, disambig_fp); fputs("\n", disambig_fp); fputs(USERTYPE_PREFIX, disambig_fp); fputs(cur_entry->typeName, disambig_fp); fputs("\n", disambig_fp); visitClassMembersNoValues(cur_entry, &printDisambigAction); fputs("\n", disambig_fp); } deleteTypeIterator(typeIt); FJALAR_DPRINTF("=> generateDisambigFile: Finished Types\n\n"); }
// Takes a location and a VariableEntry and tries to determine // the UPPER BOUND of the array which the pointer refers to. // CAUTION: This function is still fairly primitive and untested // // This now uses a two-pass scheme which first searches to the end of the // array and then goes backwards until it finds the first byte whose V-bit // is valid so that it can avoid printing out tons of garbage values and // cluttering up the .dtrace file. // // This also now has support to find statically-sized arrays within structs // declared as global and local variables as well as statically-sized arrays // which are themselves global and local variables int returnArrayUpperBoundFromPtr(VariableEntry* var, Addr varLocation) { VariableEntry* targetVar = 0; Addr baseAddr = 0; char foundGlobalArrayVariable = 0; FJALAR_DPRINTF("Checking for upper bound of %p\n", (void *)varLocation); // 1. Search if varLocation is within a global variable if (addressIsGlobal(varLocation)) { targetVar = returnArrayVariableWithAddr(&globalVars, varLocation, 1, 0, &baseAddr); if (targetVar) { foundGlobalArrayVariable = 1; } else { // UNCONDITIONALLY RETURN 0 IF WE CANNOT FIND A GLOBAL ARRAY // VARIABLE. WE DO NOT WANT TO PROBE IN THE GLOBAL SPACE // BECAUSE ALL OF IT MAY POSSIBLY BE INITIALIZED. // targetVar = returnGlobalSingletonWithAddress(varLocation); // if (targetVar) { return 0; // } } } // 2. If not found, then search if varLocation is within the stack // frame of a function currently on the stack if (!targetVar) { FunctionExecutionState* e; FJALAR_DPRINTF("Not found in globals area, checking on stack\n"); e = returnFunctionExecutionStateWithAddress(varLocation); FJALAR_DPRINTF("Found function entry %p\n", e); if (e) { VarList* localArrayAndStructVars = &(e->func->localArrayAndStructVars); FJALAR_DPRINTF(" e->FP is %p\n", (void *)e->FP); FJALAR_DPRINTF(" localArrayAndSTructVars: %p, numVars: %d\n", localArrayAndStructVars, localArrayAndStructVars->numVars); tl_assert(!localArrayAndStructVars || (Addr)localArrayAndStructVars > 0x100); if (localArrayAndStructVars && // hopefully ensures that it's not totally bogus ((Addr)localArrayAndStructVars > 0x100) && (localArrayAndStructVars->numVars > 0)) { targetVar = returnArrayVariableWithAddr(localArrayAndStructVars, varLocation, 0, e, &baseAddr); } } } // 3. If still not found, then search the heap for varLocation // if it is lower than the current frame pointer // This is a last-ditch desperation attempt and won't yield valid-looking // results in cases like when you have a pointer to an int which is located // within a struct malloc'ed on the heap. if (!targetVar) { FJALAR_DPRINTF("Not found on stack, checking in heap\n"); tl_assert(curFunctionExecutionStatePtr); FJALAR_DPRINTF("Checking if the variable is on the stack:\n"); FJALAR_DPRINTF("\tCurrent Stackframe: [%p - %p]\n", (void*)curFunctionExecutionStatePtr->FP, (void*)curFunctionExecutionStatePtr->lowestSP); // Make sure the address is not in the stack or global region // before probing so that we don't accidentally make a mistake // where we erroneously conclude that the array size is HUGE // since all areas on the stack and global regions are ALLOCATED // so probing won't do us much good if ((varLocation < curFunctionExecutionStatePtr->lowestSP) && !addressIsGlobal(varLocation)) { int size; FJALAR_DPRINTF("Location looks reasonable, probing at %p\n", (void *)varLocation); size = probeAheadDiscoverHeapArraySize(varLocation, getBytesBetweenElts(var)); FJALAR_DPRINTF("Size is %d\n", size); // We want an upper-bound on the array, not the actual size if (size > 0) return (size - 1); else return 0; } } // This is a less strict match which only compares rep. types // ... we will do more checking later to really determine the relative sizes. // This leniency allows an int* to reference a char[] and so forth ... // see below for translation // else if (baseAddr && // (targetVar->varType->repType == var->varType->repType)) { // (comment added 2005) // TODO: Hmmmm, what are we gonna do without repTypes??? I need to // investigate this 'if' condition more carefully later: else if (baseAddr) { int targetVarSize = 0; int targetVarBytesBetweenElts = getBytesBetweenElts(targetVar); int varBytesBetweenElts = getBytesBetweenElts(var); Addr highestAddr; tl_assert(IS_STATIC_ARRAY_VAR(targetVar)); FJALAR_DPRINTF("varLocation: %p\n", (void *)varLocation); highestAddr = baseAddr + ((targetVar->staticArr->upperBounds[0]) * targetVarBytesBetweenElts); FJALAR_DPRINTF("baseAddr is: %p, highestAddr is %p\n", (void *)baseAddr, (void *)highestAddr); // NEW!: Probe backwards until you find the first address whose V-bit is SET: // but ONLY do this for globals and NOT for stuff on the stack because // V-bits for stack variables are FLAKY!!! During function exit, all the V-bits // are wiped out :( if (foundGlobalArrayVariable) { while ((highestAddr > varLocation) && (MC_Ok != mc_check_readable(highestAddr, targetVarBytesBetweenElts, 0))) { highestAddr -= targetVarBytesBetweenElts; } } // This is IMPORTANT that we subtract from varLocation RATHER than baseAddr // because of the fact that varLocation can point to the MIDDLE of an array targetVarSize = (highestAddr - varLocation) / targetVarBytesBetweenElts; FJALAR_DPRINTF("targetVarBytesBetweenElts is %d, varBytesBetweenElts is %d, targetVarSize is %d\n", targetVarBytesBetweenElts, varBytesBetweenElts, targetVarSize); FJALAR_DPRINTF("Target : [%s - %d] Source : [%s - %d]\n", targetVar->varType->typeName, targetVarBytesBetweenElts, var->varType->typeName, varBytesBetweenElts); if (targetVarBytesBetweenElts == varBytesBetweenElts) { return targetVarSize; } else { return (targetVarSize * targetVarBytesBetweenElts) / varBytesBetweenElts; } } return 0; }
// Returns an array or struct variable within varList // that encompasses the address provided by "a". // Properties for return value r = &(returnNode.var): // location(r) <= "a" < location(r) + (r->upperBounds[0] * getBytesBetweenElts(r)) // [if array] // location(r) <= "a" < location(r) + (getBytesBetweenElts(r)) // [if struct] // where location(.) is the global location if isGlobal and stack location // based on FP or SP if !isGlobal // *baseAddr = the base address of the variable returned static VariableEntry* returnArrayVariableWithAddr(VarList* varList, Addr a, char isGlobal, FunctionExecutionState* e, Addr* baseAddr) { VarNode* cur_node = 0; ThreadId tid = VG_(get_running_tid)(); Addr var_loc = 0; FJALAR_DPRINTF("[returnArrayVariableWithAddr] varList: %p, Addr: %p, %s\n", varList, (void *)a, (isGlobal)?"Global":"NonGlobal"); if (!isGlobal) { FJALAR_DPRINTF("frame_ptr: %p, stack_ptr: %p\n", (void *)e->FP, (void *)e->lowSP); } for (cur_node = varList->first; cur_node != 0; cur_node = cur_node->next) { VariableEntry* potentialVar = cur_node->var; Addr potentialVarBaseAddr = 0; if (!potentialVar) continue; FJALAR_DPRINTF("Examining potential var: %s, offset: 0x%x, locType: 0x%x\n", potentialVar->name, potentialVar->byteOffset, potentialVar->locationType); if (isGlobal) { tl_assert(IS_GLOBAL_VAR(potentialVar)); potentialVarBaseAddr = potentialVar->globalVar->globalLocation; FJALAR_DPRINTF("Examining potential var address: %p\n",(void *) potentialVarBaseAddr); } else { if (potentialVar->locationType == FP_OFFSET_LOCATION) { potentialVarBaseAddr = e->FP + potentialVar->byteOffset; } else { // (comment added 2014) // Potential bug! We are ignoring other locationTypes // and just assuming it is ESP. This is the only case // we've seen (i386 only) so far. (markro) potentialVarBaseAddr = e->lowSP + potentialVar->byteOffset; } } if (potentialVar->location_expression_size) { unsigned int i = 0; for(i = 0; i < potentialVar->location_expression_size; i++ ) { dwarf_location *dloc = &(potentialVar->location_expression[i]); unsigned int op = dloc->atom; int reg_val; if(op == DW_OP_addr) { // DWARF supplied address var_loc = dloc->atom_offset; } else if(op == DW_OP_deref) { // Dereference result of last DWARF operation tl_assert(var_loc); var_loc = *(Addr *)var_loc; } else if((op >= DW_OP_const1u) && (op <= DW_OP_consts)) { // DWARF supplied constant var_loc = dloc->atom_offset; } else if((op >= DW_OP_plus) && (op <= DW_OP_plus_uconst)) { // Add DWARF supplied constant to value to result of last DWARF operation var_loc += dloc->atom_offset; } else if((op >= DW_OP_reg0) && (op <= DW_OP_reg31)) { // Get value located in architectural register reg_val = (*get_reg[dloc->atom - DW_OP_reg0])(tid); FJALAR_DPRINTF("\tObtaining register value: [%%%s]: %x\n", dwarf_reg_string[dloc->atom - DW_OP_reg0], reg_val); var_loc = (Addr)®_val; } else if((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { // Get value pointed to by architectural register reg_val = (*get_reg[dloc->atom - DW_OP_breg0])(tid); FJALAR_DPRINTF("\tObtaining register value: [%%%s]: %x\n", dwarf_reg_string[dloc->atom - DW_OP_breg0], reg_val); var_loc = reg_val + dloc->atom_offset; FJALAR_DPRINTF("\tAdding %lld to the register value for %p\n", dloc->atom_offset, (void *)var_loc); tl_assert(var_loc); } else if(op == DW_OP_fbreg) { // Get value located at an offset from the FRAME_BASE. FJALAR_DPRINTF("atom offset: %lld vs. byteOffset: %d\n", dloc->atom_offset, potentialVar->byteOffset); var_loc = e->FP + dloc->atom_offset; } else { // There's a fair number of DWARF operations still unsupported. There is a full list // in fjalar_debug.h FJALAR_DPRINTF("\tUnsupported DWARF stack OP: %s\n", location_expression_to_string(op)); tl_assert(0); } FJALAR_DPRINTF("\tApplying DWARF Stack Operation %s - %p\n",location_expression_to_string(op), (void *)var_loc); } } FJALAR_DPRINTF("addr: %p, potential var_loc: %p, staticArr: %p, ptrLevels: %d, varType: %d\n", (void*)a, (void*)potentialVarBaseAddr, potentialVar->staticArr, potentialVar->ptrLevels, (potentialVar->varType ? potentialVar->varType->decType : 0)); // array if (IS_STATIC_ARRAY_VAR(potentialVar) && (potentialVarBaseAddr <= a) && (a < (potentialVarBaseAddr + (potentialVar->staticArr->upperBounds[0] * getBytesBetweenElts(potentialVar))))) { FJALAR_DPRINTF("returnArrayVar: found matching array with upperBounds[0]: %d\n", potentialVar->staticArr->upperBounds[0]); *baseAddr = potentialVarBaseAddr; return potentialVar; } // struct else if (VAR_IS_BASE_STRUCT(potentialVar) && (potentialVarBaseAddr <= a) && (a < (potentialVarBaseAddr + getBytesBetweenElts(potentialVar)))) { return searchForArrayWithinStruct(potentialVar, potentialVarBaseAddr, a, baseAddr); } } *baseAddr = 0; return 0; }