diff --git a/valgrind/fjalar/basic-tool/basic-tool.c b/valgrind/fjalar/basic-tool/basic-tool.c index cb62d2db..d8df0360 100644 --- a/valgrind/fjalar/basic-tool/basic-tool.c +++ b/valgrind/fjalar/basic-tool/basic-tool.c @@ -94,6 +94,7 @@ void fjalar_tool_handle_function_entrance(FunctionExecutionState* f_state) { 0, 1, 0, 0, + 0, &basicAction); VG_(printf)(" Function formal parameters:\n"); @@ -104,6 +105,7 @@ void fjalar_tool_handle_function_entrance(FunctionExecutionState* f_state) { (Addr)f_state->virtualStack + f_state->virtualStackFPOffset, f_state->FP, + f_state->entryRegs, &basicAction); } @@ -116,6 +118,7 @@ void fjalar_tool_handle_function_exit(FunctionExecutionState* f_state) { 0, 0, 0, 0, + 0, &basicAction); VG_(printf)(" Function formal parameters:\n"); @@ -126,6 +129,7 @@ void fjalar_tool_handle_function_exit(FunctionExecutionState* f_state) { (Addr)f_state->virtualStack + f_state->virtualStackFPOffset, f_state->FP, + f_state->entryRegs, &basicAction); VG_(printf)(" Return value:\n"); diff --git a/valgrind/fjalar/disambig.c b/valgrind/fjalar/disambig.c index 3884f99c..1f64a83c 100644 --- a/valgrind/fjalar/disambig.c +++ b/valgrind/fjalar/disambig.c @@ -206,6 +206,7 @@ void generateDisambigFile() { 0, 0, 0, + 0, &printDisambigAction); FJALAR_DPRINTF("=> generateDisambigFile: Finished Globals\n\n"); @@ -239,6 +240,7 @@ void generateDisambigFile() { 0, 0, 0, + 0, &printDisambigAction); visitVariableGroup(FUNCTION_RETURN_VAR, @@ -246,6 +248,7 @@ void generateDisambigFile() { 0, 0, 0, + 0, &printDisambigAction); fputs("\n", disambig_fp); diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index bab7736a..23d5d978 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1451,6 +1451,14 @@ decode_location_expression (unsigned char * data, if(ll) {ll->atom = op;} + /* An op that Fjalar's evaluator cannot apply, such as an unreadable register, + DW_OP_stack_value, or DW_OP_plus (the evaluator has no operand + stack), makes a formal parameter's location unreadable. + DW_OP_piece is handled below. */ + if (ok_to_harvest && op != DW_OP_nop && op != DW_OP_piece + && !location_op_is_supported (op)) + harvest_formal_param_location_unsupported (entry); + switch (op) { case DW_OP_addr: @@ -1469,10 +1477,20 @@ decode_location_expression (unsigned char * data, break; case DW_OP_const1u: SAFE_BYTE_GET_AND_INC (uvalue, data, 1, end); + if (ok_to_harvest) + if (entry && tag_is_formal_parameter(entry->tag_name)) { + harvest_formal_param_location_atom(entry, op, uvalue); + harvest_formal_param_location_offset(entry, uvalue); + } printf ("DW_OP_const1u: %lu", (unsigned long) uvalue); break; case DW_OP_const1s: SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 1, end); + if (ok_to_harvest) + if (entry && tag_is_formal_parameter(entry->tag_name)) { + harvest_formal_param_location_atom(entry, op, svalue); + harvest_formal_param_location_offset(entry, svalue); + } printf ("DW_OP_const1s: %ld", (long) svalue); break; case DW_OP_const2u: @@ -1512,16 +1530,22 @@ decode_location_expression (unsigned char * data, printf ("DW_OP_const4s: %ld", (long) svalue); break; case DW_OP_const8u: - SAFE_BYTE_GET_AND_INC (uvalue, data, 4, end); - printf ("DW_OP_const8u: %lu ", (unsigned long) uvalue); - SAFE_BYTE_GET_AND_INC (uvalue, data, 4, end); - printf ("%lu", (unsigned long) uvalue); + SAFE_BYTE_GET_AND_INC (uvalue, data, 8, end); + if (ok_to_harvest) + if (entry && tag_is_formal_parameter(entry->tag_name)) { + harvest_formal_param_location_atom(entry, op, uvalue); + harvest_formal_param_location_offset(entry, uvalue); + } + printf ("DW_OP_const8u: %s", dwarf_vmatoa ("u", uvalue)); break; case DW_OP_const8s: - SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 4, end); - printf ("DW_OP_const8s: %ld ", (long) svalue); - SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 4, end); - printf ("%ld", (long) svalue); + SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 8, end); + if (ok_to_harvest) + if (entry && tag_is_formal_parameter(entry->tag_name)) { + harvest_formal_param_location_atom(entry, op, svalue); + harvest_formal_param_location_offset(entry, svalue); + } + printf ("DW_OP_const8s: %s", dwarf_vmatoa ("d", svalue)); break; case DW_OP_constu: READ_ULEB (uvalue, data, end); @@ -1809,6 +1833,17 @@ decode_location_expression (unsigned char * data, break; case DW_OP_piece: READ_ULEB (uvalue, data, end); + /* Fjalar can read a location that consists of one piece, if that + piece is the whole parameter; extractOneFormalParameterVar + checks the size. A piece that is not the last op means that + the parameter is split. A piece of size 0 covers no bytes. */ + if (ok_to_harvest) + { + if (data == end && uvalue != 0) + harvest_formal_param_location_piece (entry, uvalue); + else + harvest_formal_param_location_unsupported (entry); + } printf ("DW_OP_piece: %s", dwarf_vmatoa ("u", uvalue)); break; case DW_OP_deref_size: @@ -3821,6 +3856,10 @@ read_and_display_attr_value (unsigned long attribute, if (ok_to_harvest) { // frame base location list harvest_frame_base(entry, DW_OP_list, uvalue); + // Fjalar does not evaluate a location list for a formal + // parameter, so it cannot read the parameter. + if (attribute == DW_AT_location) + harvest_formal_param_location_unsupported(entry); } printf ("("); printf ("location list"); @@ -9710,6 +9749,14 @@ regname (unsigned int regno, int name_only_p) return reg; } +/* Returns the name of DWARF register REGNO, for Fjalar's debugging + output. */ +const char * +dwarf_reg_name (unsigned int regno) +{ + return regname (regno, 1); +} + static void frame_display_row (Frame_Chunk *fc, int *need_col_headers, unsigned int *max_regs) { diff --git a/valgrind/fjalar/fjalar_dwarf.c b/valgrind/fjalar/fjalar_dwarf.c index f5450e25..37758377 100644 --- a/valgrind/fjalar/fjalar_dwarf.c +++ b/valgrind/fjalar/fjalar_dwarf.c @@ -336,3 +336,21 @@ location_expression_to_string(enum dwarf_location_atom op) { return(0); // to stop compiler warning } } + +// Returns true if Fjalar's location evaluator (in visitVariableGroup) applies +// op. A register operation (DW_OP_reg* or DW_OP_breg*) is supported only if +// Fjalar can read the register; Fjalar saves such a register, with its A and +// V bits and DynComp tags, at function entrance. extractOneFormalParameterVar +// further restricts which locations that use DW_OP_reg* it accepts. +int location_op_is_supported(unsigned int op) { + if ((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { + return (op - DW_OP_breg0) < FJALAR_NUM_DWARF_REGS; + } + if ((op >= DW_OP_reg0) && (op <= DW_OP_reg31)) { + return (op - DW_OP_reg0) < FJALAR_NUM_DWARF_REGS; + } + return ((op == DW_OP_deref) + || ((op >= DW_OP_const1u) && (op <= DW_OP_consts)) + || (op == DW_OP_plus_uconst) + || (op == DW_OP_fbreg)); +} diff --git a/valgrind/fjalar/fjalar_dwarf.h b/valgrind/fjalar/fjalar_dwarf.h index f3e97557..4d4e2d7b 100644 --- a/valgrind/fjalar/fjalar_dwarf.h +++ b/valgrind/fjalar/fjalar_dwarf.h @@ -41,4 +41,17 @@ typedef struct _dwarf_location { // A conversion between DWARF location atoms and a string representation const char* location_expression_to_string(enum dwarf_location_atom op); +// One more than the largest DWARF register number that Fjalar can +// read. +#if defined(VGA_amd64) +#define FJALAR_NUM_DWARF_REGS 16 +#else +#define FJALAR_NUM_DWARF_REGS 9 +#endif + +// Returns true if visitVariableGroup can evaluate op in the location +// expression of a formal parameter. decode_location_expression +// records every such op in the formal parameter's dwarf_stack. +int location_op_is_supported(unsigned int op); + #endif /* FJALAR_DWARF_H */ diff --git a/valgrind/fjalar/fjalar_include.h b/valgrind/fjalar/fjalar_include.h index e92574ae..0e8cf3c3 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -402,6 +402,11 @@ typedef struct _VariableEntry { // location information will have validLoc = False Bool validLoc; + // True if the DWARF information gives a location that Fjalar cannot + // read (see location_has_unsupported_op in typedata.h). Then + // validLoc is False and location_expression is empty. + Bool locationUnsupported; + // In some situations (The primary one being dealing with the // formal parameters to main - see the MAIN STACK LAYOUT comment // in fjalar_traversal.c for more information) the location of @@ -690,6 +695,12 @@ typedef struct _FunctionEntry { UInt nonce; + // Bit N is set if the valid location of some formal parameter uses + // DWARF register N (via DW_OP_regN or DW_OP_bregN) and Fjalar can + // read that register. Fjalar saves those registers at function + // entrance in FunctionExecutionState.entryRegs. + UInt entryRegsMask; + } FunctionEntry; @@ -740,6 +751,36 @@ These data structures and functions provide mechanisms for runtime traversals within data structures and arrays **********************************************************************/ +#if defined(VGA_amd64) +// The DWARF register number of the frame pointer +#define DWARF_FP_REG 6 +#else +// The DWARF register number of the frame pointer +#define DWARF_FP_REG 5 +#endif + +// FJALAR_NUM_DWARF_REGS is defined in fjalar_dwarf.h. + +// If atom is DW_OP_regN or DW_OP_bregN, sets *regNum to N and returns +// True. Otherwise, returns False. +Bool dwarf_op_reg_num(UInt atom, UInt* regNum); + +// Returns true if Fjalar can read the register with the given DWARF +// register number. +Bool dwarf_reg_is_readable(UInt regNum); + +// Returns the guest-state offset of the register with the given DWARF +// register number, which must be readable. +Int dwarf_reg_guest_offset(UInt regNum); + +// Returns the current value of the register with the given DWARF +// register number, which must be readable. +Addr read_dwarf_reg(ThreadId tid, UInt regNum); + +// Returns the name of the register with the given DWARF register +// number, for debugging output (defined in dwarf.c). +const HChar* dwarf_reg_name(UInt regNum); + // Entries for tracking the runtime state of functions at entrances // and exits (used mainly by FunctionExecutionStateStack in // fjalar_main.c). This class CANNOT BE SUBCLASSED because it is @@ -789,8 +830,25 @@ typedef struct { // should be invisible to the calling function anyways. char* virtualStack; int virtualStackByteSize; // Number of 1-byte entries in virtualStack + // Number of bytes in the allocation that starts at virtualStack, + // which also contains entryRegs + int allocByteSize; int virtualStackFPOffset; // Where in the stack the frame pointer was + // The values at function entrance of the registers, indexed by + // DWARF register number: entryRegs[N] holds register N, if bit N + // of func->entryRegsMask is set. A formal parameter whose location is a + // register (DW_OP_reg*) is read from here, and the address of one + // whose location is relative to a register (DW_OP_breg*) is computed + // from here, at both entrance and exit, for the same reason as + // virtualStack: the function body and epilogue may change the + // registers. At function entrance, + // Fjalar copies those registers, with their A and V bits and + // DynComp tags, to entryRegs. entryRegs has FJALAR_NUM_DWARF_REGS + // elements and lies in the same allocation as virtualStack, just + // after it; it is null if func->entryRegsMask is 0. + Addr* entryRegs; + Addr lowSP; } FunctionExecutionState; @@ -955,6 +1013,12 @@ void visitVariableGroup(VariableOrigin varOrigin, // address space (it's what we'll dereference) Addr stackBaseAddr, Addr stackBaseAddrGuest, + // The values of the registers at function + // entrance, which is + // FunctionExecutionState.entryRegs (only + // used for varOrigin == FUNCTION_FORMAL_PARAM + // and if stackBaseAddr is non-zero) + Addr* entryRegs, // This function performs an action for each // variable visited: TraversalAction *performAction); diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index c0066a3c..4e3257d9 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -95,8 +95,8 @@ Bool doing_debug_print = False; // The filename of the target executable: const HChar* executable_filename = 0; -// Mapping between Dwarf Register numbers and -// valgrind function to return the value +// Mapping between DWARF register numbers and the guest-state offset +// of the register. // Below comment is ripped from GCC 4.3.1 /* Define the register numbers to be used in Dwarf debugging information. @@ -122,75 +122,91 @@ const HChar* executable_filename = 0; #if defined(VGA_amd64) // AMD64 Dwarf to Architecture mapping is (thankfully) specified // in the AMD64 ABI (http://x86-64.org/documentation/abi.pdf) -Addr (*get_reg[16])( ThreadId tid ) = { - VG_(get_xAX), - VG_(get_xDX), - VG_(get_xCX), - VG_(get_xBX), - VG_(get_xSI), - VG_(get_xDI), - VG_(get_FP), - VG_(get_SP), - VG_(get_R8), - VG_(get_R9), - VG_(get_R10), - VG_(get_R11), - VG_(get_R12), - VG_(get_R13), - VG_(get_R14), - VG_(get_R15), +static const Int dwarf_reg_guest_offsets[FJALAR_NUM_DWARF_REGS] = { + offsetof(VexGuestArchState, guest_RAX), + offsetof(VexGuestArchState, guest_RDX), + offsetof(VexGuestArchState, guest_RCX), + offsetof(VexGuestArchState, guest_RBX), + offsetof(VexGuestArchState, guest_RSI), + offsetof(VexGuestArchState, guest_RDI), + offsetof(VexGuestArchState, guest_RBP), + offsetof(VexGuestArchState, guest_RSP), + offsetof(VexGuestArchState, guest_R8), + offsetof(VexGuestArchState, guest_R9), + offsetof(VexGuestArchState, guest_R10), + offsetof(VexGuestArchState, guest_R11), + offsetof(VexGuestArchState, guest_R12), + offsetof(VexGuestArchState, guest_R13), + offsetof(VexGuestArchState, guest_R14), + offsetof(VexGuestArchState, guest_R15), }; #else -Addr (*get_reg[11])( ThreadId tid ) = { - VG_(get_xAX), - VG_(get_xCX), - VG_(get_xDX), - VG_(get_xBX), - VG_(get_SP), - VG_(get_FP), - VG_(get_xSI), - VG_(get_xDI), - VG_(get_IP), - NULL, - NULL +static const Int dwarf_reg_guest_offsets[FJALAR_NUM_DWARF_REGS] = { + offsetof(VexGuestArchState, guest_EAX), + offsetof(VexGuestArchState, guest_ECX), + offsetof(VexGuestArchState, guest_EDX), + offsetof(VexGuestArchState, guest_EBX), + offsetof(VexGuestArchState, guest_ESP), + offsetof(VexGuestArchState, guest_EBP), + offsetof(VexGuestArchState, guest_ESI), + offsetof(VexGuestArchState, guest_EDI), + offsetof(VexGuestArchState, guest_EIP), }; #endif -// For debugging purposes, a mapping between -// DWARF location atoms and their string -// representation -#if defined(VGA_amd64) -const HChar* dwarf_reg_string[16] = { - "xAX", - "xDX", - "xCX", - "xBX", - "xSI", - "xDI", - "xFP", - "xSP", - "R8", - "R9", - "R10", - "R11", - "R12", - "R13", - "R14", - "R15" -}; -#else -const HChar* dwarf_reg_string[9] = { - "xAX", - "xCX", - "xDX", - "xBX", - "xSP", - "xFP", - "xSI", - "xDI", - "xIP" -}; -#endif +Bool dwarf_reg_is_readable(UInt regNum) +{ + return regNum < FJALAR_NUM_DWARF_REGS; +} + +Int dwarf_reg_guest_offset(UInt regNum) +{ + tl_assert(dwarf_reg_is_readable(regNum)); + return dwarf_reg_guest_offsets[regNum]; +} + +Addr read_dwarf_reg(ThreadId tid, UInt regNum) +{ + Addr value; + tl_assert(dwarf_reg_is_readable(regNum)); + VG_(get_shadow_regs_area)(tid, (UChar*)&value, 0/*shadowNo*/, + dwarf_reg_guest_offsets[regNum], sizeof(Addr)); + return value; +} + +// If atom is DW_OP_regN (the value is in register N) or DW_OP_bregN +// (the value is at an offset from the address in register N), sets +// *regNum to N and returns True. Otherwise, returns False and leaves +// *regNum unchanged. N might not be readable by Fjalar; see +// dwarf_reg_is_readable. +Bool dwarf_op_reg_num(UInt atom, UInt* regNum) +{ + if (atom >= DW_OP_breg0 && atom <= DW_OP_breg31) { + *regNum = atom - DW_OP_breg0; + return True; + } + if (atom >= DW_OP_reg0 && atom <= DW_OP_reg31) { + *regNum = atom - DW_OP_reg0; + return True; + } + return False; +} + +// Saves the registers in f_state->func->entryRegsMask, with their A +// and V bits and DynComp tags, in f_state->entryRegs, so that formal +// parameters whose locations use registers have their entrance values +// at both entrance and exit. +static void save_param_regs(FunctionExecutionState* f_state, ThreadId tid) +{ + UInt regNum; + for (regNum = 0; regNum < FJALAR_NUM_DWARF_REGS; regNum++) { + if (f_state->func->entryRegsMask & (1U << regNum)) { + f_state->entryRegs[regNum] = read_dwarf_reg(tid, regNum); + mc_copy_guest_reg_state(tid, dwarf_reg_guest_offset(regNum), + (Addr)&f_state->entryRegs[regNum]); + } + } +} // located in VEX/priv/main_util.c extern void vex_bzero(void* s, UInt n); @@ -258,7 +274,11 @@ static void handle_possible_entry_func(MCEnv *mce, Addr64 addr, FJALAR_DPRINTF("Found a valid entry point at %x for\n", (UInt)addr); // We need all general purpose registers. +#if defined(VGA_amd64) + di->nFxState = 10; +#else di->nFxState = 9; +#endif vex_bzero(&di->fxState, sizeof(di->fxState)); di->fxState[0].fx = Ifx_Read; @@ -290,6 +310,12 @@ static void handle_possible_entry_func(MCEnv *mce, Addr64 addr, di->fxState[8].fx = Ifx_Read; di->fxState[8].offset = mce->layout->offset_xDI; di->fxState[8].size = mce->layout->sizeof_xDI; +#if defined(VGA_amd64) + // R8 through R15, which are contiguous in the guest state + di->fxState[9].fx = Ifx_Read; + di->fxState[9].offset = offsetof(VexGuestArchState, guest_R8); + di->fxState[9].size = 8 * sizeof(ULong); +#endif stmt('V', mce, IRStmt_Dirty(di) ); } @@ -483,7 +509,11 @@ void handle_possible_exit(MCEnv* mce, IRJumpKind jk) { // 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. +#if defined(VGA_amd64) + di->nFxState = 12; +#else di->nFxState = 11; +#endif vex_bzero(&di->fxState, sizeof(di->fxState)); di->fxState[0].fx = Ifx_Read; @@ -522,6 +552,12 @@ void handle_possible_exit(MCEnv* mce, IRJumpKind jk) { di->fxState[10].fx = Ifx_Read; di->fxState[10].offset = offsetof(VexGuestArchState, guest_FPREG); di->fxState[10].size = 8 * sizeof(ULong); +#if defined(VGA_amd64) + // R8 through R15, which are contiguous in the guest state + di->fxState[11].fx = Ifx_Read; + di->fxState[11].offset = offsetof(VexGuestArchState, guest_R8); + di->fxState[11].size = 8 * sizeof(ULong); +#endif stmt('V', mce, IRStmt_Dirty(di) ); } @@ -553,6 +589,7 @@ void enter_function(FunctionEntry* f) Addr stack_ptr= VG_(get_SP)(tid); Addr frame_ptr = 0; /* E.g., %ebp */ int local_stack, size; + UInt regNum; FJALAR_DPRINTF("[enter_function] startPC is: %x, entryPC is: %x, cu_base: %p\n", (UInt)f->startPC, (UInt)f->entryPC,(void *)f->cuBase); @@ -619,13 +656,19 @@ void enter_function(FunctionEntry* f) // 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) + // is to just assume the frame base is just above the saved frame + // pointer and the return address, as is normal: EBP+8 on x86 and + // RBP+16 on amd64. (markro) if (ll->atom == DW_OP_deref) { - ll->atom = DW_OP_breg5; - ll->atom_offset = 8; + ll->atom = DW_OP_breg0 + DWARF_FP_REG; + ll->atom_offset = 2 * sizeof(Addr); } - if(get_reg[ll->atom - DW_OP_breg0]) { - frame_ptr = (*get_reg[ll->atom - DW_OP_breg0])(tid) + ll->atom_offset; + // For DW_OP_regN, the offset is 0. + if (dwarf_op_reg_num(ll->atom, ®Num) && dwarf_reg_is_readable(regNum)) { + frame_ptr = read_dwarf_reg(tid, regNum) + ll->atom_offset; + } else { + FJALAR_DPRINTF("\tCannot compute frame base from location list entry with atom %s\n", + location_expression_to_string(ll->atom)); } } } @@ -637,14 +680,24 @@ void enter_function(FunctionEntry* f) FJALAR_DPRINTF("\tExamining frame: %lx - %lx\n", df->begin, df->end); if (f->entryPC >= df->begin && f->entryPC < df->end) { FJALAR_DPRINTF("\tFound debug frame with reg %x and offset %lx\n", df->cfa_reg, df->cfa_offset); - frame_ptr = (*get_reg[df->cfa_reg])(tid) + df->cfa_offset; + if (dwarf_reg_is_readable(df->cfa_reg)) { + frame_ptr = read_dwarf_reg(tid, df->cfa_reg) + df->cfa_offset; + } else { + FJALAR_DPRINTF("\tCannot compute frame base from CFA register %s\n", + dwarf_reg_name(df->cfa_reg)); + } break; } df = df->next; } } else { FJALAR_DPRINTF("\tsimple location expression\n"); - frame_ptr = (*get_reg[f->frame_base_atom - DW_OP_reg0])(tid) + f->frame_base_offset; + if (dwarf_op_reg_num(f->frame_base_atom, ®Num) && dwarf_reg_is_readable(regNum)) { + frame_ptr = read_dwarf_reg(tid, regNum) + f->frame_base_offset; + } else { + FJALAR_DPRINTF("\tCannot compute frame base from atom %s\n", + location_expression_to_string(f->frame_base_atom)); + } } } @@ -712,7 +765,18 @@ void enter_function(FunctionEntry* f) tl_assert(size >= 0); if (size != 0) { - newEntry->virtualStack = VG_(calloc)("fjalar_main.c: enter_func", size, sizeof(char)); + // entryRegs follows the virtual stack in the same allocation. + Int entryRegsOffset = VG_ROUNDUP(size, sizeof(Addr)); + Int entryRegsSize = (f->entryRegsMask != 0 + ? FJALAR_NUM_DWARF_REGS * sizeof(Addr) + : 0); + newEntry->allocByteSize = entryRegsOffset + entryRegsSize; + newEntry->virtualStack = VG_(calloc)("fjalar_main.c: enter_func", + newEntry->allocByteSize, + sizeof(char)); + newEntry->entryRegs = (f->entryRegsMask != 0 + ? (Addr*)(newEntry->virtualStack + entryRegsOffset) + : NULL); newEntry->virtualStackByteSize = size; newEntry->virtualStackFPOffset = local_stack; @@ -737,6 +801,9 @@ void enter_function(FunctionEntry* f) newEntry->func->guestStackEnd = newEntry->func->guestStackStart + size; newEntry->func->lowestVirtSP = (Addr)newEntry->virtualStack; + if (f->entryRegsMask != 0) { + save_param_regs(newEntry, tid); + } } else { printf("Obtained a stack size of 0 for Function: %s. Aborting\n", f->fjalar_name); @@ -912,7 +979,7 @@ void exit_function(FunctionEntry* f) 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); + mc_make_noaccess((Addr)top->virtualStack, top->allocByteSize); VG_(free)(top->virtualStack); } diff --git a/valgrind/fjalar/fjalar_main.h b/valgrind/fjalar/fjalar_main.h index 80df7b1b..897a3c57 100644 --- a/valgrind/fjalar/fjalar_main.h +++ b/valgrind/fjalar/fjalar_main.h @@ -185,26 +185,6 @@ This is called from hooks within mac_shared.h char* fjalar_program_stdout_filename; char* fjalar_program_stderr_filename; -// Mapping between Dwarf Register numbers and -// valgrind function to return the value - -#if defined(VGA_amd64) -// AMD64 Dwarf to Architecture mapping is (thankfully) specified -// in the AMD64 ABI (http://x86-64.org/documentation/abi.pdf) -extern Addr (*get_reg[16])( ThreadId tid ); -// The DWARF register number of the stack pointer -#define DWARF_SP_REG 7 -#else -extern Addr (*get_reg[11])( ThreadId tid ); -// The DWARF register number of the stack pointer -#define DWARF_SP_REG 4 -#endif - -// For debugging purposes, a mapping between -// DWARF location atoms and their string -// representation - -extern const HChar* dwarf_reg_string[]; /* diff --git a/valgrind/fjalar/fjalar_select.c b/valgrind/fjalar/fjalar_select.c index c75d5e35..474dcb02 100644 --- a/valgrind/fjalar/fjalar_select.c +++ b/valgrind/fjalar/fjalar_select.c @@ -355,6 +355,7 @@ void outputVariableNamesToFile() { 0, 0, 0, + 0, &printVarNameAction); fputs("\n", var_dump_fp); @@ -385,6 +386,7 @@ void outputVariableNamesToFile() { 0, 0, 0, + 0, &printVarNameAction); visitVariableGroup(FUNCTION_RETURN_VAR, @@ -392,6 +394,7 @@ void outputVariableNamesToFile() { 0, 0, 0, + 0, &printVarNameAction); fputs("\n", var_dump_fp); diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index c64786cf..5ca3942a 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -864,6 +864,7 @@ void visitVariableGroup(VariableOrigin varOrigin, Bool isEnter, // 1 for function entrance, 0 for exit Addr stackBaseAddr, // should only be used for FUNCTION_FORMAL_PARAM Addr stackBaseAddrGuest,// should only be used for FUNCTION_FORMAL_PARAM + Addr* entryRegs, // should only be used for FUNCTION_FORMAL_PARAM // This function performs an action for each // variable visited: TraversalAction *performAction) { @@ -929,18 +930,18 @@ void visitVariableGroup(VariableOrigin varOrigin, continue; } - if ((varOrigin == FUNCTION_FORMAL_PARAM) && stackBaseAddr) { - ThreadId tid = VG_(get_running_tid)(); - - // (comment added 2009) - // HACKISH. needed to work around bad location information in - // the DWARF tables, while still providing tools with the variables - // if they care. return is an exception as we figure it out ourself. - if(!var->validLoc && !VG_STREQ("return", var->name)) { - FJALAR_DPRINTF("\t[visitVariableGroup] invalid loc, punting\n"); - continue; - } + // Omit a formal parameter whose location Fjalar cannot read. This + // test does not depend on stackBaseAddr, so that the .decls file + // (which is written without a stack) declares exactly the + // variables that the .dtrace file contains. return is an + // exception, as we figure it out ourself. + if ((varOrigin == FUNCTION_FORMAL_PARAM) && + !var->validLoc && !VG_STREQ("return", var->name)) { + FJALAR_DPRINTF("\t[visitVariableGroup] invalid loc, punting\n"); + continue; + } + if ((varOrigin == FUNCTION_FORMAL_PARAM) && stackBaseAddr) { FJALAR_DPRINTF("\t[visitVariableGroup] baseAddr: %p, baseAddrGuest: %p var->byteOffset: %x(%d)\n", (void *)stackBaseAddr, (void *)stackBaseAddrGuest, (unsigned int)var->byteOffset, var->byteOffset); FJALAR_DPRINTF("\t[visitVariableGroup] State of Guest Stack [%p - %p] \n", (void *)funcPtr->guestStackStart, (void *)funcPtr->guestStackEnd); FJALAR_DPRINTF("\t[visitVariableGroup] State of Virtual Stack [%p - %p] \n", (void *)funcPtr->lowestVirtSP, (void *)(funcPtr->lowestVirtSP + (funcPtr->guestStackEnd - funcPtr->guestStackStart))); @@ -1006,16 +1007,13 @@ void visitVariableGroup(VariableOrigin varOrigin, else { // Dwarf Locations are implemented as a sequence of operations to be performed. + // Every op satisfies location_op_is_supported. for(i = 0; i < var->location_expression_size; i++ ) { dwarf_location *dloc = &(var->location_expression[i]); unsigned int op = dloc->atom; - Addr reg_val; - - if(op == DW_OP_addr) { - // DWARF supplied address - var_loc = dloc->atom_offset; + UInt regNum; - } else if(op == DW_OP_deref) { + if(op == DW_OP_deref) { // Dereference result of last DWARF operation tl_assert(var_loc); var_loc = *(Addr *)var_loc; @@ -1024,32 +1022,36 @@ void visitVariableGroup(VariableOrigin varOrigin, // DWARF supplied constant var_loc = dloc->atom_offset; - } else if((op >= DW_OP_plus) && (op <= DW_OP_plus_uconst)) { + } else if(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]: %p\n", dwarf_reg_string[dloc->atom - DW_OP_reg0], - (void *)reg_val); - var_loc = (Addr)®_val; - - } else if((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { - // Get value pointed to by architectural register - if (dloc->atom - DW_OP_breg0 == DWARF_SP_REG) { - // Use the stack pointer at function entry (after the - // prologue). At function exit, the epilogue has already - // popped the stack frame. - reg_val = funcPtr->guestStackStart + VG_STACK_REDZONE_SZB; + } else if(dwarf_op_reg_num(op, ®Num)) { + // DW_OP_regN: the value is in register N. + // DW_OP_bregN: the value is at an offset from register N's value. + // Either way, use the value that the register held at + // function entrance (after the prologue), which is saved + // (with its A and V bits and tags) in the + // FunctionExecutionState. At function exit, the body may + // have overwritten the register and the epilogue has + // already restored or popped it. + Bool isBreg = (op >= DW_OP_breg0) && (op <= DW_OP_breg31); + // extractOneFormalParameterVar() saves every register that + // a valid location uses. It sets locationUnsupported for any + // other register location, which keeps the main and this + // heuristics from making the location valid. + tl_assert(entryRegs && (funcPtr->entryRegsMask & (1U << regNum))); + if (isBreg) { + Addr reg_val = entryRegs[regNum]; + FJALAR_DPRINTF("\tObtaining entrance register value: [%%%s]: %p\n", dwarf_reg_name(regNum), + (void *)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 { - reg_val = (*get_reg[dloc->atom - DW_OP_breg0])(tid); + FJALAR_DPRINTF("\tObtaining entrance value of register: [%%%s]\n", dwarf_reg_name(regNum)); + var_loc = (Addr)&entryRegs[regNum]; } - FJALAR_DPRINTF("\tObtaining register value: [%%%s]: %p\n", dwarf_reg_string[dloc->atom - DW_OP_breg0], - (void *)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. @@ -1057,8 +1059,7 @@ void visitVariableGroup(VariableOrigin varOrigin, var_loc = stackBaseAddrGuest + dloc->atom_offset; } else { - // There's a fair number of DWARF operations still unsupported. There is a full list - // in fjalar_debug.h + // location_op_is_supported must agree with this evaluator. FJALAR_DPRINTF("\tUnsupported DWARF stack OP: %s\n", location_expression_to_string(op)); tl_assert(0); } @@ -1088,8 +1089,15 @@ void visitVariableGroup(VariableOrigin varOrigin, var->entryLocGuest = var_loc; basePtrValueGuest = var_loc; } else { + // The location is not on the stack, so Fjalar reads the + // variable where it is. For a register location, that is + // entryRegs, which has no guest address; using its address + // as the guest address gives a static array within the + // variable a non-null address. basePtrValue = var_loc; var->entryLoc = var_loc; + basePtrValueGuest = var_loc; + var->entryLocGuest = var_loc; } } } diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index fc89f77d..345d630d 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2360,6 +2360,21 @@ static void extractLocalArrayAndStructVariables(FunctionEntry* f, dwarfFunctionEntry->name); } +// Returns true if the DWARF location of var places its value (or, for +// a location made of DW_OP_piece operations, its first piece) in a +// register. This holds for every var whose locationType is +// REGISTER_LOCATION, and also for some whose location Fjalar does not +// support, such as an XMM register or a struct split across registers. +static Bool isInRegister(VariableEntry* var) +{ + UInt atom; + if (var->location_expression_size == 0) { + return False; + } + atom = var->location_expression[0].atom; + return atom >= DW_OP_reg0 && atom <= DW_OP_reg31; +} + // MUST BE RUN AFTER the return value for a function has been initialized!!! // Otherwise it cannot tell whether the function returns a struct type!!! // @@ -2403,6 +2418,11 @@ static void verifyStackParamWordAlignment(FunctionEntry* f, int replace) cur_node = cur_node->next) { int cur_byteSize = 0; + // A parameter in a register occupies no stack slot, even if + // Fjalar cannot read the register. + if (isInRegister(cur_node->var)) { + continue; + } if (cur_node->var->locationType == NO_LOCATION || replace) { FJALAR_DPRINTF("MODIFY VAR LOCATION! was:\n"); FJALAR_DPRINTF(" decType is: %s, size is: %d\n", DeclaredTypeString[cur_node->var->varType->decType], @@ -2516,6 +2536,61 @@ int determineFormalParametersLowerStackByteSize(FunctionEntry* f) } +// Returns true if the formal parameter's location is a register +// whose entrance value Fjalar can save and from which Fjalar can read +// the whole value. The location must be a single DW_OP_reg operation: +// Fjalar does not support any other operation (such as DW_OP_lit*) +// that is not a memory location. +static Bool isSupportedRegisterLocation(formal_parameter* paramPtr, + VariableEntry* varPtr) +{ + enum dwarf_location_atom atom; + int byteSize; + + if (paramPtr->dwarf_stack_size != 1) { + return False; + } + atom = paramPtr->dwarf_stack[0].atom; + if (atom < DW_OP_reg0 || atom > DW_OP_reg31 || + !dwarf_reg_is_readable(atom - DW_OP_reg0)) { + return False; + } + // A C++ reference is passed as a pointer. + byteSize = (varPtr->referenceLevels > 0 + ? (int)sizeof(Addr) + : determineVariableByteSize(varPtr)); + return 0 < byteSize && byteSize <= (int)sizeof(Addr); +} + +// Adds to f->entryRegsMask each readable register that the formal +// parameter's location uses, so that Fjalar saves the register at +// function entrance. +static void recordEntryRegs(FunctionEntry* f, formal_parameter* paramPtr) +{ + unsigned int i; + for (i = 0; i < paramPtr->dwarf_stack_size; i++) { + UInt regNum; + if (dwarf_op_reg_num(paramPtr->dwarf_stack[i].atom, ®Num) + && dwarf_reg_is_readable(regNum)) { + f->entryRegsMask |= 1U << regNum; + } + } +} + +// Returns true if the formal parameter's location uses a register +// (DW_OP_reg* or DW_OP_breg*). +static Bool locationUsesRegister(formal_parameter* paramPtr) +{ + unsigned int i; + UInt regNum; + for (i = 0; i < paramPtr->dwarf_stack_size; i++) { + if (dwarf_op_reg_num(paramPtr->dwarf_stack[i].atom, ®Num)) { + return True; + } + } + return False; +} + // dwarfParamEntry->tag_name == DW_TAG_formal_parameter static void extractOneFormalParameterVar(FunctionEntry* f, dwarf_entry* dwarfParamEntry) @@ -2562,24 +2637,48 @@ static void extractOneFormalParameterVar(FunctionEntry* f, 0,0,0,0,0,0,0,1, 0); - if (paramPtr->dwarf_stack_size > 0) { - unsigned int i; - FJALAR_DPRINTF("\tCopying over DWARF Location stack\n"); - for(i = 0; i < paramPtr->dwarf_stack_size; i++) { - varPtr->location_expression[i].atom = paramPtr->dwarf_stack[i].atom; - varPtr->location_expression[i].atom_offset = paramPtr->dwarf_stack[i].atom_offset; - FJALAR_DPRINTF("\tCopying over %s (%lld)\n", location_expression_to_string(varPtr->location_expression[i].atom), varPtr->location_expression[i].atom_offset); + if (paramPtr->location_has_unsupported_op + || ((paramPtr->location_piece_size != 0) + && (paramPtr->location_piece_size + != (varPtr->ptrLevels > 0 ? sizeof(void*) + : (unsigned long)varPtr->varType->byteSize)))) { + // varPtr->validLoc remains 0, so Fjalar omits the formal parameter. + FJALAR_DPRINTF(" location contains an unsupported operation\n"); + varPtr->locationUnsupported = 1; + } else { + if (paramPtr->dwarf_stack_size > 0) { + unsigned int i; + FJALAR_DPRINTF("\tCopying over DWARF Location stack\n"); + for(i = 0; i < paramPtr->dwarf_stack_size; i++) { + varPtr->location_expression[i].atom = paramPtr->dwarf_stack[i].atom; + varPtr->location_expression[i].atom_offset = paramPtr->dwarf_stack[i].atom_offset; + FJALAR_DPRINTF("\tCopying over %s (%lld)\n", location_expression_to_string(varPtr->location_expression[i].atom), varPtr->location_expression[i].atom_offset); + } + varPtr->location_expression_size = paramPtr->dwarf_stack_size; } - varPtr->location_expression_size = paramPtr->dwarf_stack_size; - } - - if (paramPtr->location_type == LT_FP_OFFSET) { - varPtr->validLoc = paramPtr->valid_loc; - varPtr->locationType = FP_OFFSET_LOCATION; - varPtr->byteOffset = paramPtr->location; - // varPtr->atom = paramPtr->loc_atom; - FJALAR_DPRINTF(" location_type: %u, byteOffset: %x\n", varPtr->locationType, (unsigned int)varPtr->byteOffset); + if (paramPtr->location_type == LT_FP_OFFSET) { + varPtr->validLoc = paramPtr->valid_loc; + varPtr->locationType = FP_OFFSET_LOCATION; + varPtr->byteOffset = paramPtr->location; + // varPtr->atom = paramPtr->loc_atom; + recordEntryRegs(f, paramPtr); + + FJALAR_DPRINTF(" location_type: %u, byteOffset: %x\n", varPtr->locationType, (unsigned int)varPtr->byteOffset); + } else if (isSupportedRegisterLocation(paramPtr, varPtr)) { + UInt regNum = paramPtr->dwarf_stack[0].atom - DW_OP_reg0; + varPtr->validLoc = 1; + varPtr->locationType = REGISTER_LOCATION; + recordEntryRegs(f, paramPtr); + FJALAR_DPRINTF(" location is register %s\n", dwarf_reg_name(regNum)); + } else if (locationUsesRegister(paramPtr)) { + // A register location that Fjalar cannot read, such as a register + // combined with other operations or a value larger than a register. + // Fjalar did not save the register at entrance, so no heuristic may + // make the location valid. + FJALAR_DPRINTF(" location is a register that Fjalar cannot read\n"); + varPtr->locationUnsupported = 1; + } } FJALAR_DPRINTF("EXIT extractOneFormalParameterVar\n"); @@ -3148,6 +3247,7 @@ static void processFunctions() { if ((f->formalParameters.numVars > 0) && (f->formalParameters.first->var) && !(f->formalParameters.first->var->validLoc) && + !(f->formalParameters.first->var->locationUnsupported) && (f->formalParameters.first->var->varType->decType == D_INT)){ VariableEntry* var = f->formalParameters.first->var; printf("Found invalid argc, varByteOffset is: %d\n", var->byteOffset); @@ -3159,6 +3259,7 @@ static void processFunctions() { if ((f->formalParameters.numVars > 1) && (f->formalParameters.first->next->var) && !(f->formalParameters.first->next->var->validLoc) && + !(f->formalParameters.first->next->var->locationUnsupported) && (f->formalParameters.first->next->var->varType->decType == D_CHAR)) { VariableEntry* var = f->formalParameters.first->next->var; printf("Found invalid argv, varByteOffset is: %d\n", var->byteOffset); @@ -3210,7 +3311,9 @@ static void initConstructorsAndDestructors(void) { // (comment added 2009) // RUDD - Dirty dirty hack. Force the location of this to be valid - f->formalParameters.first->var->validLoc = 1; + if (!f->formalParameters.first->var->locationUnsupported) { + f->formalParameters.first->var->validLoc = 1; + } // Use the regular (not mangled or demangled) name for matching diff --git a/valgrind/fjalar/kvasir/decls-output.c b/valgrind/fjalar/kvasir/decls-output.c index 51a3e98b..6394d9b6 100644 --- a/valgrind/fjalar/kvasir/decls-output.c +++ b/valgrind/fjalar/kvasir/decls-output.c @@ -347,6 +347,7 @@ void DC_outputDeclsAtEnd() { 0, 0, 0, + 0, &printDeclsEntryAction); fputs("\n", decls_fp); genfreehashtable(varsDeclaredTable); @@ -1242,6 +1243,7 @@ printDeclsEntryAction(VariableEntry* var, isEnter, 0, 0, + 0, (faux_decls ? &nullAction : &printDeclsEntryAction)); @@ -1251,6 +1253,7 @@ printDeclsEntryAction(VariableEntry* var, isEnter, 0, 0, + 0, (faux_decls ? &nullAction : &printDeclsEntryAction)); @@ -1261,6 +1264,7 @@ printDeclsEntryAction(VariableEntry* var, 0, 0, 0, + 0, (faux_decls ? &nullAction : &printDeclsEntryAction)); } @@ -1515,6 +1519,7 @@ printDeclsEntryAction(VariableEntry* var, 0, 0, 0, + 0, &printDeclsEntryAction); fputs("\n", decls_fp); genfreehashtable(varsDeclaredTable); @@ -1803,6 +1808,7 @@ static void harvestOneFunctionObject(FunctionEntry* func, struct genhashtable* o False, 0, 0, + 0, &harvestObject); visitVariableGroup(FUNCTION_FORMAL_PARAM, @@ -1810,6 +1816,7 @@ static void harvestOneFunctionObject(FunctionEntry* func, struct genhashtable* o True, 0, 0, + 0, &harvestObject); cur_object_table = NULL; diff --git a/valgrind/fjalar/kvasir/dtrace-output.c b/valgrind/fjalar/kvasir/dtrace-output.c index bbadf0c4..2e147143 100644 --- a/valgrind/fjalar/kvasir/dtrace-output.c +++ b/valgrind/fjalar/kvasir/dtrace-output.c @@ -1241,6 +1241,7 @@ static int exit_count = 0; isEnter, 0, 0, + 0, &printDtraceEntryAction); // print_info = 0; @@ -1252,6 +1253,7 @@ static int exit_count = 0; (Addr)f_state->virtualStack + f_state->virtualStackFPOffset, f_state->FP, + f_state->entryRegs, &printDtraceEntryAction); // If isEnter == 0, print out return value: diff --git a/valgrind/fjalar/kvasir/dyncomp_runtime.c b/valgrind/fjalar/kvasir/dyncomp_runtime.c index acb716cd..fb20bbf1 100644 --- a/valgrind/fjalar/kvasir/dyncomp_runtime.c +++ b/valgrind/fjalar/kvasir/dyncomp_runtime.c @@ -694,6 +694,7 @@ static void DC_extra_propagate_one_function(FunctionEntry* funcPtr, isEnter, 0, 0, + 0, &dyncompExtraPropAction); // Propagate through formal params. @@ -702,6 +703,7 @@ static void DC_extra_propagate_one_function(FunctionEntry* funcPtr, isEnter, 0, 0, + 0, &dyncompExtraPropAction); // If EXIT, propagate through return value @@ -711,6 +713,7 @@ static void DC_extra_propagate_one_function(FunctionEntry* funcPtr, 0, 0, 0, + 0, &dyncompExtraPropAction); } } diff --git a/valgrind/fjalar/mc_include.h b/valgrind/fjalar/mc_include.h index ab62b1e2..eaed7a8d 100644 --- a/valgrind/fjalar/mc_include.h +++ b/valgrind/fjalar/mc_include.h @@ -76,6 +76,10 @@ void set_abit_and_vbyte ( Addr a, UWord abit, UWord vbyte ); extern void mc_copy_address_range_state ( Addr src, Addr dst, SizeT len ); void mc_make_noaccess ( Addr a, SizeT len ); +// Copies the A and V bits and the DynComp tag of the pointer-sized +// guest register at guestOffset in thread tid's guest state to the +// sizeof(Addr) bytes of memory at dst. +void mc_copy_guest_reg_state ( ThreadId tid, Int guestOffset, Addr dst ); extern char mc_are_some_bytes_initialized (Addr a, SizeT len); Bool mc_check_writable ( Addr a, SizeT len, Addr* bad_addr ); diff --git a/valgrind/fjalar/mc_main.c b/valgrind/fjalar/mc_main.c index 6cbc2d8c..2818569b 100644 --- a/valgrind/fjalar/mc_main.c +++ b/valgrind/fjalar/mc_main.c @@ -978,6 +978,26 @@ void mc_make_noaccess ( Addr a, SizeT len ) MC_(make_mem_noaccess) ( a, len ); } +void mc_copy_guest_reg_state ( ThreadId tid, Int guestOffset, Addr dst ) +{ + UWord vbits; + SizeT b; + VG_(get_shadow_regs_area)(tid, (UChar*)&vbits, 1/*shadowNo*/, + guestOffset, sizeof(Addr)); + for (b = 0; b < sizeof(Addr); b++) { + set_abit_and_vbyte(dst + b, VGM_BIT_VALID, (vbits >> (b * 8)) & 0xff); + } +#ifndef _NO_DYNCOMP + if (kvasir_with_dyncomp) { + // DynComp keeps one tag per register, at the register's offset. + UInt tag = *VG_(get_tag_ptr_for_guest_offset)(tid, guestOffset); + for (b = 0; b < sizeof(Addr); b++) { + set_tag(dst + b, tag); + } + } +#endif /* _NO_DYNCOMP */ +} + // end of code added for Fjalar /* --------------- Secondary V bit table ------------ */ diff --git a/valgrind/fjalar/tests/register-params-test.sh b/valgrind/fjalar/tests/register-params-test.sh new file mode 100755 index 00000000..36c2d47a --- /dev/null +++ b/valgrind/fjalar/tests/register-params-test.sh @@ -0,0 +1,159 @@ +#!/bin/bash + +# Tests that Fjalar traces formal parameters whose location is a register. +# +# In optimized code, a formal parameter's location may be a register +# (DW_OP_reg*) rather than a stack slot. Fjalar must output such a parameter's +# value at function entrance and, like a parameter on the stack, the same +# value at function exit. DynComp must compute its comparability. +# +# Also tests that Fjalar omits a formal parameter whose location it cannot +# read, from both the .decls file and the .dtrace file. + +set -e + +test_dir="$(cd "$(dirname "$0")" && pwd)" +valgrind="${test_dir}/../../inst/bin/valgrind" + +if [ ! -x "${valgrind}" ]; then + echo "$0: Fjalar is not built; run \"make build\" at the top level." >&2 + exit 2 +fi + +output_dir="$(mktemp -d)" +trap 'rm -rf "${output_dir}"' EXIT + +# Fjalar does not read DWARF 5, and it recognizes a function's entry point +# only at the address in the debugging information, so the executable must not +# be position-independent. -gno-variable-location-views keeps location view +# pairs out of the .debug_loc section. +gcc -gdwarf-4 -gno-variable-location-views -no-pie -O1 -c \ + -o "${output_dir}/register-params.o" "${test_dir}/register-params.c" +# stack-params.c is a separate compilation unit, because an unoptimized +# function in register-params.c changes the comparability of scale's +# parameters. +gcc -gdwarf-4 -gno-variable-location-views -no-pie -O0 -S -dA \ + -o "${output_dir}/stack-params.s" "${test_dir}/stack-params.c" + +# gcc does not emit DW_OP_stack_value after DW_OP_fbreg for a formal parameter +# of an out-of-line function, so rewrite the location of stack_value's v from +# "DW_OP_fbreg N", where N needs 2 bytes, to "DW_OP_fbreg -8; +# DW_OP_stack_value". The size of the debugging information is unchanged, so +# the offsets that refer to it remain correct. +awk '/DW_AT_name/ && /"v\\0"/ && !done { state = 1 } + state == 1 && /# DW_OP_fbreg/ { state = 2; print; next } + state == 2 { + if ($1 != ".sleb128" || $2 >= -64 || $2 < -8192) { + print "unexpected location for v: " $0 > "/dev/stderr"; exit 1 + } + print "\t.sleb128 -8" + print "\t.byte\t0x9f\t# DW_OP_stack_value" + state = 0; done = 1; next + } + { print } + END { if (!done) { print "did not find v" > "/dev/stderr"; exit 1 } }' \ + "${output_dir}/stack-params.s" > "${output_dir}/stack-params-v.s" + +# Renames formal parameter $1, which has 3 characters, to $2, which has 1 +# character, and appends "DW_OP_piece $3" to its location, which is +# "DW_OP_fbreg N" where N needs 1 byte. The size of the debugging information +# is unchanged. +append_piece() { + awk -v name="$1" -v newname="$2" -v size="$3" ' + /DW_AT_name/ && index($0, "\"" name "\\0\"") && !done { + sub(name, newname); state = 1; print; next } + state == 1 && /# DW_AT_location/ { + if ($2 != "0x2") { + print "unexpected location for " name ": " $0 > "/dev/stderr"; exit 1 + } + print "\t.uleb128 0x4\t# DW_AT_location"; state = 2; next + } + state == 2 && /# DW_OP_fbreg/ { state = 3; print; next } + state == 3 { + print + print "\t.byte\t0x93\t# DW_OP_piece" + print "\t.uleb128 " size + state = 0; done = 1; next + } + { print } + END { if (!done) { print "did not find " name > "/dev/stderr"; exit 1 } }' +} +append_piece w__ w 8 < "${output_dir}/stack-params-v.s" \ + | append_piece p__ p 4 > "${output_dir}/stack-params-patched.s" +gcc -gdwarf-4 -no-pie -o "${output_dir}/register-params" \ + "${output_dir}/register-params.o" "${output_dir}/stack-params-patched.s" + +# register-params.goal assumes that the location of every formal parameter of +# an optimized function is a register throughout its function. Another compiler version might instead +# use a location list, DW_OP_entry_value, or a constant, or might omit the +# location, for which Fjalar omits the formal parameter. A line that starts +# with ":" begins a new debugging information entry. +if readelf --debug-dump=info "${output_dir}/register-params" \ + | awk 'function end_param() { if (inparam && !hasloc) bad = 1; inparam = 0 } + /^ *<[0-9]+><[0-9a-f]+>:/ { end_param() } + /DW_TAG_subprogram/ { insub = 1; next } + insub && /DW_AT_name/ { fname = $NF; insub = 0 } + # These functions are in stack-params.c, which is not optimized. + fname ~ /^(stack_value|kept|whole_piece|part_piece)$/ { next } + /DW_TAG_formal_parameter/ { inparam = 1; hasloc = 0; next } + inparam && /DW_AT_location/ { hasloc = 1 } + inparam && /DW_AT_location/ && !/: [0-9]+ byte block: .*\(DW_OP_reg/ { bad = 1 } + inparam && /DW_AT_const_value/ { bad = 1 } + END { end_param(); exit !bad }'; then + readelf --debug-dump=info "${output_dir}/register-params" \ + | grep -A8 DW_TAG_formal_parameter >&2 + # In continuous integration (GitHub Actions sets CI, and Azure Pipelines + # sets TF_BUILD), fail rather than skip, so that a compiler change that + # makes this test vacuous does not go unnoticed. + if [ -n "${CI:-}" ] || [ -n "${TF_BUILD:-}" ]; then + echo "$0: FAILED: gcc did not put every formal parameter in a register" >&2 + exit 1 + fi + echo "$0: SKIPPED: gcc did not put every formal parameter in a register" >&2 + exit 0 +fi + +decls="${output_dir}/register-params.decls" +dtrace="${output_dir}/register-params.dtrace" +status=0 +"${valgrind}" --tool=fjalar --dyncomp \ + --decls-file="${decls}" --dtrace-file="${dtrace}" \ + "${output_dir}/register-params" \ + > "${output_dir}/register-params.out" 2>&1 || status=$? + +if [ "${status}" -ne 0 ]; then + echo "$0: FAILED: Fjalar exited with status ${status}" >&2 + cat "${output_dir}/register-params.out" >&2 + exit 1 +fi + +# One line per non-global variable at the first occurrence of each program +# point other than main's (that is, in the first call to each function): +# " ", followed by one line per non-global variable +# declared at each program point other than main's: " +# comparability ". The latter shows that Fjalar omits a formal +# parameter whose location it cannot read from the .decls file too. The order +# of program points in the .decls file depends on function addresses, so the +# latter lines are stably sorted by program point. +actual="${output_dir}/register-params.actual" +awk '/^\.\.[a-z_]*\(\):::/ { ppt = $0; getline; getline + if (ppt ~ /^\.\.main\(/ || seen[ppt]++) ppt = "" + next } + ppt == "" { next } + /^$/ { ppt = ""; next } + { var = $0; getline; value = $0; getline + if (var !~ /^::/) print ppt, var, value }' \ + "${dtrace}" > "${actual}" +awk '/^ppt / { ppt = $2 } + /^ variable / { var = $2 } + var ~ /^::/ { next } + /^ comparability / { if (ppt !~ /^\.\.main\(/) + print ppt, var, "comparability", $2 }' \ + "${decls}" | LC_ALL=C sort -s -k1,1 >> "${actual}" + +if ! diff -u "${test_dir}/register-params.goal" "${actual}" >&2; then + echo "$0: FAILED: output differs from register-params.goal" >&2 + exit 1 +fi + +echo "$0: PASSED" diff --git a/valgrind/fjalar/tests/register-params.c b/valgrind/fjalar/tests/register-params.c new file mode 100644 index 00000000..4ec1ae12 --- /dev/null +++ b/valgrind/fjalar/tests/register-params.c @@ -0,0 +1,68 @@ +/* + This file is part of Fjalar, a dynamic analysis framework for C/C++ + programs. + + Copyright (C) 2026 University of Washington Computer Science & Engineering Department, + Programming Languages and Software Engineering Group + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. +*/ + +// A C program that, when compiled with optimization, has formal parameters +// whose location is a register (DW_OP_reg*) throughout the function, which +// Fjalar traces, and formal parameters whose locations Fjalar cannot read, +// which Fjalar omits from both the .decls and .dtrace files. Together with +// stack-params.c. See register-params-test.sh. + +// a and b are comparable to each other and to the result. +__attribute__((noinline)) int add(int a, int b) { + return a + b; +} + +// x is comparable to neither factor nor the result. +__attribute__((noinline)) int scale(int x, int factor) { + return factor * 2; +} + +struct pair { long first; long second; }; + +volatile long sink; + +// p is split across two registers (DW_OP_piece), which Fjalar does not +// support, so p is omitted from both the .decls and .dtrace files. +__attribute__((noinline)) void split(struct pair p) { + sink = p.first; + sink = p.second; +} + +// d is in a floating-point register, which Fjalar cannot read, so d is +// omitted from both the .decls and .dtrace files. +__attribute__((noinline)) double identity(double d) { + return d; +} + +// Defined in stack-params.c, which register-params-test.sh compiles without +// optimization. See register-params-test.sh. +void stack_value(long v); +void kept(long y); +void whole_piece(long w__); +void part_piece(long p__); + +int main(void) { + int t = 0; + for (int i = 1; i < 4; i++) { + t += add(i, 3); + t += scale(i, 7); + } + struct pair p = {5, 2}; + split(p); + stack_value(17); + kept(13); + whole_piece(14); + part_piece(15); + t += (int)identity(1.5); + return t == 12345; +} diff --git a/valgrind/fjalar/tests/register-params.goal b/valgrind/fjalar/tests/register-params.goal new file mode 100644 index 00000000..62e38b31 --- /dev/null +++ b/valgrind/fjalar/tests/register-params.goal @@ -0,0 +1,30 @@ +..add():::ENTER a 1 +..add():::ENTER b 3 +..add():::EXIT0 a 1 +..add():::EXIT0 b 3 +..add():::EXIT0 return 4 +..scale():::ENTER x 1 +..scale():::ENTER factor 7 +..scale():::EXIT0 x 1 +..scale():::EXIT0 factor 7 +..scale():::EXIT0 return 14 +..kept():::ENTER y 13 +..kept():::EXIT0 y 13 +..whole_piece():::ENTER w 14 +..whole_piece():::EXIT0 w 14 +..identity():::EXIT0 return 1.5 +..add():::ENTER a comparability 2 +..add():::ENTER b comparability 2 +..add():::EXIT0 a comparability 2 +..add():::EXIT0 b comparability 2 +..add():::EXIT0 return comparability 2 +..identity():::EXIT0 return comparability 2 +..kept():::ENTER y comparability 1 +..kept():::EXIT0 y comparability 1 +..scale():::ENTER x comparability 2 +..scale():::ENTER factor comparability 3 +..scale():::EXIT0 x comparability 2 +..scale():::EXIT0 factor comparability 3 +..scale():::EXIT0 return comparability 3 +..whole_piece():::ENTER w comparability 1 +..whole_piece():::EXIT0 w comparability 1 diff --git a/valgrind/fjalar/tests/stack-params.c b/valgrind/fjalar/tests/stack-params.c new file mode 100644 index 00000000..449eb3bf --- /dev/null +++ b/valgrind/fjalar/tests/stack-params.c @@ -0,0 +1,47 @@ +/* + This file is part of Fjalar, a dynamic analysis framework for C/C++ + programs. + + Copyright (C) 2026 University of Washington Computer Science & Engineering Department, + Programming Languages and Software Engineering Group + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. +*/ + +// Formal parameters in the stack frame (DW_OP_fbreg), whose locations +// register-params-test.sh rewrites. register-params-test.sh compiles this +// file without optimization, separately from register-params.c. + +extern volatile long sink; + +// v is in the stack frame (DW_OP_fbreg). register-params-test.sh +// rewrites v's location to "DW_OP_fbreg -8; DW_OP_stack_value", which Fjalar +// does not support, so v is omitted. +void stack_value(long v) { + // buf makes v's frame offset large enough to need 2 bytes. + volatile char buf[200]; + buf[0] = 1; + sink = v + buf[0]; +} + +// y is in the stack frame (DW_OP_fbreg), which Fjalar supports, +// so y is not omitted. +void kept(long y) { + sink = y; +} + +// w__ and p__ are in the stack frame (DW_OP_fbreg). +// register-params-test.sh renames them to w and p, and it appends +// "DW_OP_piece 8" to w's location and "DW_OP_piece 4" to p's location. +// Fjalar supports a location that is one piece covering the whole parameter, +// so w is not omitted, but p is omitted. +void whole_piece(long w__) { + sink = w__; +} + +void part_piece(long p__) { + sink = p__; +} diff --git a/valgrind/fjalar/typedata.c b/valgrind/fjalar/typedata.c index aefb8829..30e8d7b9 100644 --- a/valgrind/fjalar/typedata.c +++ b/valgrind/fjalar/typedata.c @@ -1203,7 +1203,10 @@ char harvest_formal_param_location_atom(dwarf_entry* e, enum dwarf_location_atom formal_parameter *paramPtr = ((formal_parameter*)e->entry_ptr); paramPtr->loc_atom = atom; - tl_assert(paramPtr->dwarf_stack_size < MAX_DWARF_OPS); + if (paramPtr->dwarf_stack_size == MAX_DWARF_OPS) { + paramPtr->location_has_unsupported_op = 1; + return 1; + } paramPtr->dwarf_stack[paramPtr->dwarf_stack_size].atom = atom; paramPtr->dwarf_stack[paramPtr->dwarf_stack_size].atom_offset = value; paramPtr->dwarf_stack_size++; @@ -1234,6 +1237,42 @@ char harvest_formal_param_location_offset(dwarf_entry* e, long value) return 0; } +// If e is a formal parameter, records that Fjalar cannot read its location +// (sets location_has_unsupported_op), so Fjalar omits the parameter from the +// .decls and .dtrace files. Returns 1 if e is a formal parameter, 0 +// otherwise. +char harvest_formal_param_location_unsupported(dwarf_entry* e) +{ + if ((e == 0) || (e->entry_ptr == 0)) + return 0; + + if (tag_is_formal_parameter(e->tag_name)) + { + ((formal_parameter*)e->entry_ptr)->location_has_unsupported_op = 1; + return 1; + } + else + return 0; +} + +// If e is a formal parameter, records that its location expression ends +// with "DW_OP_piece size" (sets location_piece_size). +// extractOneFormalParameterVar omits the parameter unless the piece is the +// whole parameter. Returns 1 if e is a formal parameter, 0 otherwise. +char harvest_formal_param_location_piece(dwarf_entry* e, unsigned long size) +{ + if ((e == 0) || (e->entry_ptr == 0)) + return 0; + + if (tag_is_formal_parameter(e->tag_name)) + { + ((formal_parameter*)e->entry_ptr)->location_piece_size = size; + return 1; + } + else + return 0; +} + char harvest_data_member_location(dwarf_entry* e, unsigned long value) { unsigned long tag; @@ -1914,6 +1953,8 @@ static void process_abstract_origin_items(void) aliased_formal_param->location_type = cur_param->location_type; aliased_formal_param->loc_atom = cur_param->loc_atom; aliased_formal_param->valid_loc = cur_param->valid_loc; + aliased_formal_param->location_has_unsupported_op = cur_param->location_has_unsupported_op; + aliased_formal_param->location_piece_size = cur_param->location_piece_size; aliased_formal_param->dwarf_stack_size = cur_param->dwarf_stack_size; VG_(memcpy)(aliased_formal_param->dwarf_stack, cur_param->dwarf_stack, diff --git a/valgrind/fjalar/typedata.h b/valgrind/fjalar/typedata.h index c49e9f3a..c03e0b83 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -347,6 +347,19 @@ typedef struct // way to get the parameter location unsigned int valid_loc; + // True if the location expression contains an operation that does + // not satisfy location_op_is_supported, such as a register that Fjalar cannot read, + // DW_OP_stack_value, or a DW_OP_piece that is not the last operation or + // that has size 0, + // or contains more than MAX_DWARF_OPS operations, or is a location list. + // Fjalar cannot read such a location. + char location_has_unsupported_op; + + // If the location expression ends with DW_OP_piece, the size of that + // piece; otherwise 0. Fjalar can read the location only if the piece is + // the whole parameter. + unsigned long location_piece_size; + unsigned long abstract_origin_ID; // See comment in the function struct definition // for the uses of this. @@ -568,6 +581,8 @@ char harvest_mangled_name(dwarf_entry* e, const char* str); char harvest_comp_dir(dwarf_entry* e, const char* str); char harvest_producer(dwarf_entry* e, const char* str); char harvest_formal_param_location_offset(dwarf_entry* e, long value); +char harvest_formal_param_location_unsupported(dwarf_entry* e); +char harvest_formal_param_location_piece(dwarf_entry* e, unsigned long size); char harvest_formal_param_location_atom(dwarf_entry* e, enum dwarf_location_atom atom, long value); char harvest_data_member_location(dwarf_entry* e, unsigned long value); char harvest_string(dwarf_entry* e, unsigned long attr, const char* str);