From 0f270d8b26be28ff863b3bd4605b20db68f283fa Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 13:01:52 -0700 Subject: [PATCH 01/23] Trace formal parameters whose location is a register In optimized code, a formal parameter's location may be a register (DW_OP_reg*). Fjalar marked such a parameter's location invalid, so it omitted the parameter from the .dtrace file (though not from the .decls file). Even with a valid location, the traversal pointed at a local copy of the register's current value, which had no A and V bits or DynComp tags, and which at function exit no longer held the parameter. At function entrance, save the registers in the FunctionExecutionState, with their A and V bits and (for DynComp) their tags. Read a register parameter from that copy at both entrance and exit, as Fjalar reads stack parameters from the virtual stack. The entrance helper now also declares that it reads R8 through R15 on amd64. Add a regression test, register-params-test.sh. --- Makefile | 1 + valgrind/fjalar/fjalar_include.h | 20 ++++++ valgrind/fjalar/fjalar_main.c | 65 +++++++++++++++++- valgrind/fjalar/fjalar_main.h | 4 +- valgrind/fjalar/fjalar_traversal.c | 24 +++++-- valgrind/fjalar/generate_fjalar_entries.c | 3 +- valgrind/fjalar/kvasir/kvasir_main.c | 20 ++++++ valgrind/fjalar/tests/register-params-test.sh | 67 +++++++++++++++++++ valgrind/fjalar/tests/register-params.c | 35 ++++++++++ valgrind/fjalar/tests/register-params.goal | 16 +++++ 10 files changed, 245 insertions(+), 10 deletions(-) create mode 100755 valgrind/fjalar/tests/register-params-test.sh create mode 100644 valgrind/fjalar/tests/register-params.c create mode 100644 valgrind/fjalar/tests/register-params.goal diff --git a/Makefile b/Makefile index 284f4bad..55684f29 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ test: # Fjalar tests that do not depend on Daikon fjalar-test: valgrind/fjalar/tests/library-compilation-unit-test.sh + valgrind/fjalar/tests/register-params-test.sh # Kvasir tests daikon-test: ../daikon diff --git a/valgrind/fjalar/fjalar_include.h b/valgrind/fjalar/fjalar_include.h index e92574ae..073bfead 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -740,6 +740,18 @@ These data structures and functions provide mechanisms for runtime traversals within data structures and arrays **********************************************************************/ +// The number of DWARF register numbers that Fjalar can read (the +// size of the get_reg array in fjalar_main.c). +#if defined(VGA_amd64) +#define FJALAR_NUM_DWARF_REGS 16 +#else +#define FJALAR_NUM_DWARF_REGS 11 +#endif + +// Mapping between DWARF register numbers and the offset of the +// register in the guest state, or -1 if there is no such register +extern Int dwarf_reg_guest_offset[FJALAR_NUM_DWARF_REGS]; + // 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 @@ -791,6 +803,14 @@ typedef struct { int virtualStackByteSize; // Number of 1-byte entries in virtualStack int virtualStackFPOffset; // Where in the stack the frame pointer was + // The values of the registers at function entrance, indexed by + // DWARF register number. A formal parameter whose location is a + // register (DW_OP_reg*) is read from here, at both entrance and + // exit, for the same reason as virtualStack. At function entrance, + // Fjalar copies the registers' A and V bits to entryRegs, and + // DynComp copies their tags. + Addr entryRegs[FJALAR_NUM_DWARF_REGS]; + Addr lowSP; } FunctionExecutionState; diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index c0066a3c..aa5c8e27 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -122,7 +122,7 @@ 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 ) = { +Addr (*get_reg[FJALAR_NUM_DWARF_REGS])( ThreadId tid ) = { VG_(get_xAX), VG_(get_xDX), VG_(get_xCX), @@ -140,8 +140,26 @@ Addr (*get_reg[16])( ThreadId tid ) = { VG_(get_R14), VG_(get_R15), }; +Int dwarf_reg_guest_offset[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 ) = { +Addr (*get_reg[FJALAR_NUM_DWARF_REGS])( ThreadId tid ) = { VG_(get_xAX), VG_(get_xCX), VG_(get_xDX), @@ -154,6 +172,19 @@ Addr (*get_reg[11])( ThreadId tid ) = { NULL, NULL }; +Int dwarf_reg_guest_offset[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), + -1, + -1 +}; #endif // For debugging purposes, a mapping between @@ -258,7 +289,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 +325,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) ); } @@ -541,6 +582,7 @@ void enter_function(FunctionEntry* f) { FunctionExecutionState* newEntry; extern FunctionExecutionState* curFunctionExecutionStatePtr; + int i; // Only do enter_function if this is the first time we have // reached the preferred entry point after entering the function. @@ -679,6 +721,25 @@ void enter_function(FunctionEntry* f) newEntry->invocation_nonce = cur_nonce++; newEntry->func->nonce = newEntry->invocation_nonce; + // Save the registers, with their A and V bits, so that formal + // parameters located in registers have their entrance values at + // both entrance and exit. + for (i = 0; i < FJALAR_NUM_DWARF_REGS; i++) { + Addr regAddr = (Addr)&newEntry->entryRegs[i]; + UWord vbits = V_BITS64_UNDEFINED; + UInt b; + if (get_reg[i]) { + newEntry->entryRegs[i] = (*get_reg[i])(tid); + VG_(get_shadow_regs_area)(tid, (UChar*)&vbits, 1/*shadowNo*/, + dwarf_reg_guest_offset[i], sizeof(Addr)); + } else { + newEntry->entryRegs[i] = 0; + } + for (b = 0; b < sizeof(Addr); b++) { + set_abit_and_vbyte(regAddr + b, VGM_BIT_VALID, (vbits >> (b * 8)) & 0xff); + } + } + // FJALAR VIRTUAL STACK // Fjalar maintains a virtual stack for invocation a function. This // allows Fjalar to provide tools with unaltered values of formal diff --git a/valgrind/fjalar/fjalar_main.h b/valgrind/fjalar/fjalar_main.h index 80df7b1b..9ea1981f 100644 --- a/valgrind/fjalar/fjalar_main.h +++ b/valgrind/fjalar/fjalar_main.h @@ -191,11 +191,11 @@ char* fjalar_program_stderr_filename; #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 ); +extern Addr (*get_reg[FJALAR_NUM_DWARF_REGS])( ThreadId tid ); // The DWARF register number of the stack pointer #define DWARF_SP_REG 7 #else -extern Addr (*get_reg[11])( ThreadId tid ); +extern Addr (*get_reg[FJALAR_NUM_DWARF_REGS])( ThreadId tid ); // The DWARF register number of the stack pointer #define DWARF_SP_REG 4 #endif diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index c64786cf..083695ac 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -30,6 +30,10 @@ #include "pub_tool_threadstate.h" +// The state of the function invocation whose entrance or exit is +// being processed (defined in fjalar_runtime.c) +extern FunctionExecutionState* curFunctionExecutionStatePtr; + // This increments every time a call to visitSingleVar() or // visitSequence() is made. It is up to the caller to reset this // properly! @@ -1029,11 +1033,21 @@ void visitVariableGroup(VariableOrigin varOrigin, 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; + // Get the value that the architectural register held at + // function entrance, which is saved (with its A and V + // bits and tags) in the FunctionExecutionState. + unsigned int regNum = op - DW_OP_reg0; + FJALAR_DPRINTF("\tObtaining entrance value of register: [%%%s]\n", dwarf_reg_string[regNum]); + if (regNum < FJALAR_NUM_DWARF_REGS && get_reg[regNum] && + curFunctionExecutionStatePtr && + curFunctionExecutionStatePtr->func == funcPtr) { + var_loc = (Addr)&curFunctionExecutionStatePtr->entryRegs[regNum]; + } else { + // A register that Fjalar cannot read, such as a + // floating-point register: the value is nonsensical. + var_loc = 0; + break; + } } else if((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { // Get value pointed to by architectural register diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index fc89f77d..fbcce8a4 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2573,8 +2573,9 @@ static void extractOneFormalParameterVar(FunctionEntry* f, varPtr->location_expression_size = paramPtr->dwarf_stack_size; } + varPtr->validLoc = paramPtr->valid_loc; + 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; diff --git a/valgrind/fjalar/kvasir/kvasir_main.c b/valgrind/fjalar/kvasir/kvasir_main.c index b0b7d538..ca17cf79 100644 --- a/valgrind/fjalar/kvasir/kvasir_main.c +++ b/valgrind/fjalar/kvasir/kvasir_main.c @@ -805,6 +805,26 @@ void fjalar_tool_handle_function_entrance(FunctionExecutionState* f_state) { kvasir_late_init(); kvasir_late_init_done = True; } + + if (kvasir_with_dyncomp) { + ThreadId currentTID = VG_(get_running_tid)(); + + // For DynComp, set the tags of the saved register values + int i; + UInt b; + + for (i = 0; i < FJALAR_NUM_DWARF_REGS; i++) { + UInt regTag = 0; + if (dwarf_reg_guest_offset[i] >= 0) { + regTag = *(VG_(get_tag_ptr_for_guest_offset)(currentTID, + dwarf_reg_guest_offset[i])); + } + for (b = 0; b < sizeof(Addr); b++) { + set_tag((Addr)(&(f_state->entryRegs[i])) + (Addr)b, regTag); + } + } + } + printDtraceForFunction(f_state, 1); } diff --git a/valgrind/fjalar/tests/register-params-test.sh b/valgrind/fjalar/tests/register-params-test.sh new file mode 100755 index 00000000..2af3a35d --- /dev/null +++ b/valgrind/fjalar/tests/register-params-test.sh @@ -0,0 +1,67 @@ +#!/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. + +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, because some versions of Fjalar +# misread them. +gcc -gdwarf-4 -gno-variable-location-views -no-pie -O1 \ + -o "${output_dir}/register-params" "${test_dir}/register-params.c" + +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 variable at each program point of the invocations with nonces 1 +# and 2 (the first calls to add and scale): " ", +# followed by one line per variable at each exit program point other than +# main's: " comparability ". +actual="${output_dir}/register-params.actual" +awk '/^\.\.[a-z]*\(\):::/ { ppt = $0; getline; getline; nonce = $0; next } + ppt == "" { next } + /^$/ { ppt = ""; next } + { var = $0; getline; value = $0; getline + if (nonce == 1 || nonce == 2) print ppt, var, value }' \ + "${dtrace}" > "${actual}" +awk '/^ppt / { ppt = $2 } + /^ variable / { var = $2 } + /^ comparability / { if (ppt ~ /:::EXIT/ && ppt !~ /^\.\.main\(/) + print ppt, var, "comparability", $2 }' \ + "${decls}" >> "${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..59efcf7c --- /dev/null +++ b/valgrind/fjalar/tests/register-params.c @@ -0,0 +1,35 @@ +/* + 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. +// 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; +} + +int main(void) { + int t = 0; + for (int i = 1; i < 4; i++) { + t += add(i, 3); + t += scale(i, 7); + } + 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..b42a1263 --- /dev/null +++ b/valgrind/fjalar/tests/register-params.goal @@ -0,0 +1,16 @@ +..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 +..scale():::EXIT0 x comparability 1 +..scale():::EXIT0 factor comparability 2 +..scale():::EXIT0 return comparability 2 +..add():::EXIT0 a comparability 1 +..add():::EXIT0 b comparability 1 +..add():::EXIT0 return comparability 1 From 2d34e1ab475ee03068bf0aa8c74b8d199e3e440f Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 13:28:13 -0700 Subject: [PATCH 02/23] Address code review of register-parameter tracing - Treat a register location as valid only if it is a single DW_OP_reg of a readable register, the value fits in one register, and the location has no DW_OP_piece. Other locations (e.g., DW_OP_lit*) are skipped instead of reaching an assertion failure. - Replace get_reg and dwarf_reg_string by one table, with bounds-checked accessors used by all callers. - Save and tag only the registers that hold a function's formal parameters (FunctionEntry.paramRegMask). - Remove the unused DWARF evaluation loop in returnArrayVariableWithAddr, and skip register-located parameters there. - Assert that the current execution state matches the function being traversed. - Test a struct split across registers and a floating-point parameter, and skip the test if the compiler does not put every parameter in a register. --- valgrind/fjalar/dwarf.c | 2 + valgrind/fjalar/fjalar_include.h | 32 ++- valgrind/fjalar/fjalar_main.c | 187 +++++++----------- valgrind/fjalar/fjalar_main.h | 13 -- valgrind/fjalar/fjalar_runtime.c | 67 +------ valgrind/fjalar/fjalar_traversal.c | 32 +-- valgrind/fjalar/generate_fjalar_entries.c | 38 +++- valgrind/fjalar/kvasir/kvasir_main.c | 9 +- valgrind/fjalar/tests/register-params-test.sh | 35 +++- valgrind/fjalar/tests/register-params.c | 20 ++ valgrind/fjalar/tests/register-params.goal | 18 +- valgrind/fjalar/typedata.c | 15 ++ valgrind/fjalar/typedata.h | 5 + 13 files changed, 249 insertions(+), 224 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index 080057cd..c286843b 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1809,6 +1809,8 @@ decode_location_expression (unsigned char * data, break; case DW_OP_piece: READ_ULEB (uvalue, data, end); + if (ok_to_harvest) + harvest_formal_param_location_piece(entry); printf ("DW_OP_piece: %s", dwarf_vmatoa ("u", uvalue)); break; case DW_OP_deref_size: diff --git a/valgrind/fjalar/fjalar_include.h b/valgrind/fjalar/fjalar_include.h index 073bfead..33873bc9 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -690,6 +690,10 @@ typedef struct _FunctionEntry { UInt nonce; + // A bit mask of the DWARF register numbers of the registers that + // hold formal parameters (see FunctionExecutionState.entryRegs). + UInt paramRegMask; + } FunctionEntry; @@ -740,17 +744,30 @@ These data structures and functions provide mechanisms for runtime traversals within data structures and arrays **********************************************************************/ -// The number of DWARF register numbers that Fjalar can read (the -// size of the get_reg array in fjalar_main.c). +// One more than the largest DWARF register number that Fjalar can +// read. It must be at most 32, because FunctionEntry.paramRegMask is +// a bit mask indexed by DWARF register number. #if defined(VGA_amd64) #define FJALAR_NUM_DWARF_REGS 16 #else #define FJALAR_NUM_DWARF_REGS 11 #endif -// Mapping between DWARF register numbers and the offset of the -// register in the guest state, or -1 if there is no such register -extern Int dwarf_reg_guest_offset[FJALAR_NUM_DWARF_REGS]; +// 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. +const HChar* dwarf_reg_name(UInt regNum); // Entries for tracking the runtime state of functions at entrances // and exits (used mainly by FunctionExecutionStateStack in @@ -807,8 +824,9 @@ typedef struct { // DWARF register number. A formal parameter whose location is a // register (DW_OP_reg*) is read from here, at both entrance and // exit, for the same reason as virtualStack. At function entrance, - // Fjalar copies the registers' A and V bits to entryRegs, and - // DynComp copies their tags. + // Fjalar copies the registers in func->paramRegMask, with their A + // and V bits, to entryRegs, and DynComp copies their tags. The + // other elements are not set. Addr entryRegs[FJALAR_NUM_DWARF_REGS]; diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index aa5c8e27..c493b090 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 +// and name of the register. // Below comment is ripped from GCC 4.3.1 /* Define the register numbers to be used in Dwarf debugging information. @@ -119,109 +119,71 @@ const HChar* executable_filename = 0; */ +typedef struct { + Int guestOffset; // -1 if Fjalar cannot read the register + const HChar* name; // for debugging output +} DwarfRegInfo; + #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[FJALAR_NUM_DWARF_REGS])( 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), -}; -Int dwarf_reg_guest_offset[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), +static const DwarfRegInfo dwarf_regs[FJALAR_NUM_DWARF_REGS] = { + { offsetof(VexGuestArchState, guest_RAX), "xAX" }, + { offsetof(VexGuestArchState, guest_RDX), "xDX" }, + { offsetof(VexGuestArchState, guest_RCX), "xCX" }, + { offsetof(VexGuestArchState, guest_RBX), "xBX" }, + { offsetof(VexGuestArchState, guest_RSI), "xSI" }, + { offsetof(VexGuestArchState, guest_RDI), "xDI" }, + { offsetof(VexGuestArchState, guest_RBP), "xFP" }, + { offsetof(VexGuestArchState, guest_RSP), "xSP" }, + { offsetof(VexGuestArchState, guest_R8), "R8" }, + { offsetof(VexGuestArchState, guest_R9), "R9" }, + { offsetof(VexGuestArchState, guest_R10), "R10" }, + { offsetof(VexGuestArchState, guest_R11), "R11" }, + { offsetof(VexGuestArchState, guest_R12), "R12" }, + { offsetof(VexGuestArchState, guest_R13), "R13" }, + { offsetof(VexGuestArchState, guest_R14), "R14" }, + { offsetof(VexGuestArchState, guest_R15), "R15" }, }; #else -Addr (*get_reg[FJALAR_NUM_DWARF_REGS])( 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 -}; -Int dwarf_reg_guest_offset[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), - -1, - -1 +static const DwarfRegInfo dwarf_regs[FJALAR_NUM_DWARF_REGS] = { + { offsetof(VexGuestArchState, guest_EAX), "xAX" }, + { offsetof(VexGuestArchState, guest_ECX), "xCX" }, + { offsetof(VexGuestArchState, guest_EDX), "xDX" }, + { offsetof(VexGuestArchState, guest_EBX), "xBX" }, + { offsetof(VexGuestArchState, guest_ESP), "xSP" }, + { offsetof(VexGuestArchState, guest_EBP), "xFP" }, + { offsetof(VexGuestArchState, guest_ESI), "xSI" }, + { offsetof(VexGuestArchState, guest_EDI), "xDI" }, + { offsetof(VexGuestArchState, guest_EIP), "xIP" }, + { -1, "eflags" }, + { -1, "trapno" }, }; #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 && dwarf_regs[regNum].guestOffset >= 0; +} + +Int dwarf_reg_guest_offset(UInt regNum) +{ + tl_assert(dwarf_reg_is_readable(regNum)); + return dwarf_regs[regNum].guestOffset; +} + +Addr read_dwarf_reg(ThreadId tid, UInt regNum) +{ + Addr value; + VG_(get_shadow_regs_area)(tid, (UChar*)&value, 0/*shadowNo*/, + dwarf_reg_guest_offset(regNum), sizeof(Addr)); + return value; +} + +const HChar* dwarf_reg_name(UInt regNum) +{ + return regNum < FJALAR_NUM_DWARF_REGS ? dwarf_regs[regNum].name : "unknown"; +} // located in VEX/priv/main_util.c extern void vex_bzero(void* s, UInt n); @@ -666,8 +628,8 @@ void enter_function(FunctionEntry* f) 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; + if(dwarf_reg_is_readable(ll->atom - DW_OP_breg0)) { + frame_ptr = read_dwarf_reg(tid, ll->atom - DW_OP_breg0) + ll->atom_offset; } } } @@ -679,14 +641,18 @@ 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; + } 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_reg_is_readable(f->frame_base_atom - DW_OP_reg0)) { + frame_ptr = read_dwarf_reg(tid, f->frame_base_atom - DW_OP_reg0) + f->frame_base_offset; + } } } @@ -721,20 +687,19 @@ void enter_function(FunctionEntry* f) newEntry->invocation_nonce = cur_nonce++; newEntry->func->nonce = newEntry->invocation_nonce; - // Save the registers, with their A and V bits, so that formal - // parameters located in registers have their entrance values at - // both entrance and exit. + // Save the registers that hold formal parameters, with their A and + // V bits, so that those formal parameters have their entrance values + // at both entrance and exit. for (i = 0; i < FJALAR_NUM_DWARF_REGS; i++) { Addr regAddr = (Addr)&newEntry->entryRegs[i]; - UWord vbits = V_BITS64_UNDEFINED; + UWord vbits; UInt b; - if (get_reg[i]) { - newEntry->entryRegs[i] = (*get_reg[i])(tid); - VG_(get_shadow_regs_area)(tid, (UChar*)&vbits, 1/*shadowNo*/, - dwarf_reg_guest_offset[i], sizeof(Addr)); - } else { - newEntry->entryRegs[i] = 0; + if (!(f->paramRegMask & (1U << i))) { + continue; } + newEntry->entryRegs[i] = read_dwarf_reg(tid, i); + VG_(get_shadow_regs_area)(tid, (UChar*)&vbits, 1/*shadowNo*/, + dwarf_reg_guest_offset(i), sizeof(Addr)); for (b = 0; b < sizeof(Addr); b++) { set_abit_and_vbyte(regAddr + b, VGM_BIT_VALID, (vbits >> (b * 8)) & 0xff); } diff --git a/valgrind/fjalar/fjalar_main.h b/valgrind/fjalar/fjalar_main.h index 9ea1981f..40c6bf8a 100644 --- a/valgrind/fjalar/fjalar_main.h +++ b/valgrind/fjalar/fjalar_main.h @@ -185,27 +185,14 @@ 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[FJALAR_NUM_DWARF_REGS])( ThreadId tid ); // The DWARF register number of the stack pointer #define DWARF_SP_REG 7 #else -extern Addr (*get_reg[FJALAR_NUM_DWARF_REGS])( 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[]; - /* It is not at all clear how we should number the FP stack registers diff --git a/valgrind/fjalar/fjalar_runtime.c b/valgrind/fjalar/fjalar_runtime.c index f910808e..d37049a3 100644 --- a/valgrind/fjalar/fjalar_runtime.c +++ b/valgrind/fjalar/fjalar_runtime.c @@ -190,8 +190,6 @@ returnArrayVariableWithAddr(VarList* varList, 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) { @@ -210,6 +208,11 @@ returnArrayVariableWithAddr(VarList* varList, FJALAR_DPRINTF("Examining potential var: %s, offset: 0x%x, locType: 0x%x\n", potentialVar->name, (unsigned int) potentialVar->byteOffset, potentialVar->locationType); + // A formal parameter located in a register is not in memory, so + // it cannot contain a. + if (potentialVar->locationType == REGISTER_LOCATION) + continue; + if (isGlobal) { tl_assert(IS_GLOBAL_VAR(potentialVar)); potentialVarBaseAddr = potentialVar->globalVar->globalLocation; @@ -225,65 +228,7 @@ returnArrayVariableWithAddr(VarList* varList, potentialVarBaseAddr = e->lowSP + potentialVar->byteOffset; } } - // FJALAR_DPRINTF("potential var expression size: %u\n", potentialVar->location_expression_size); - 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; - Addr 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]: %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). - reg_val = e->lowSP; - } else { - reg_val = (*get_reg[dloc->atom - DW_OP_breg0])(tid); - } - 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. - 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: %u, varType: %u\n", + FJALAR_DPRINTF("addr: %p, potential base addr: %p, staticArr: %p, ptrLevels: %u, varType: %u\n", (void*)a, (void*)potentialVarBaseAddr, potentialVar->staticArr, potentialVar->ptrLevels, (potentialVar->varType ? potentialVar->varType->decType : 0)); diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index 083695ac..92f06c07 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -1037,29 +1037,39 @@ void visitVariableGroup(VariableOrigin varOrigin, // function entrance, which is saved (with its A and V // bits and tags) in the FunctionExecutionState. unsigned int regNum = op - DW_OP_reg0; - FJALAR_DPRINTF("\tObtaining entrance value of register: [%%%s]\n", dwarf_reg_string[regNum]); - if (regNum < FJALAR_NUM_DWARF_REGS && get_reg[regNum] && - curFunctionExecutionStatePtr && - curFunctionExecutionStatePtr->func == funcPtr) { - var_loc = (Addr)&curFunctionExecutionStatePtr->entryRegs[regNum]; - } else { - // A register that Fjalar cannot read, such as a - // floating-point register: the value is nonsensical. + FJALAR_DPRINTF("\tObtaining entrance value of register: [%%%s]\n", dwarf_reg_name(regNum)); + if (!(funcPtr->paramRegMask & (1U << regNum))) { + // extractOneFormalParameterVar() did not record the + // register in paramRegMask, so it was not saved, but + // some heuristic marked the location valid anyway. + FJALAR_DPRINTF("\tRegister %s was not saved at entrance; value is nonsensical\n", + dwarf_reg_name(regNum)); var_loc = 0; break; } + // Every caller that supplies stackBaseAddr is processing + // the entrance or exit of the current invocation. + tl_assert(curFunctionExecutionStatePtr && + curFunctionExecutionStatePtr->func == funcPtr); + var_loc = (Addr)&curFunctionExecutionStatePtr->entryRegs[regNum]; } 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) { + unsigned int regNum = op - DW_OP_breg0; + if (regNum == 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_reg_is_readable(regNum)) { + reg_val = read_dwarf_reg(tid, regNum); } else { - reg_val = (*get_reg[dloc->atom - DW_OP_breg0])(tid); + FJALAR_DPRINTF("\tCannot read register %s; value is nonsensical\n", + dwarf_reg_name(regNum)); + var_loc = 0; + break; } - FJALAR_DPRINTF("\tObtaining register value: [%%%s]: %p\n", dwarf_reg_string[dloc->atom - DW_OP_breg0], + FJALAR_DPRINTF("\tObtaining 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); diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index fbcce8a4..370d43a2 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2403,7 +2403,8 @@ static void verifyStackParamWordAlignment(FunctionEntry* f, int replace) cur_node = cur_node->next) { int cur_byteSize = 0; - if (cur_node->var->locationType == NO_LOCATION || replace) { + if (cur_node->var->locationType == NO_LOCATION || + (replace && cur_node->var->locationType != REGISTER_LOCATION)) { FJALAR_DPRINTF("MODIFY VAR LOCATION! was:\n"); FJALAR_DPRINTF(" decType is: %s, size is: %d\n", DeclaredTypeString[cur_node->var->varType->decType], cur_node->var->varType->byteSize); @@ -2516,6 +2517,32 @@ 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, nor a value split into pieces. +static Bool isSupportedRegisterLocation(formal_parameter* paramPtr, + VariableEntry* varPtr) +{ + enum dwarf_location_atom atom; + int byteSize; + + if (paramPtr->dwarf_stack_size != 1 || paramPtr->location_has_piece) { + 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); +} + // dwarfParamEntry->tag_name == DW_TAG_formal_parameter static void extractOneFormalParameterVar(FunctionEntry* f, dwarf_entry* dwarfParamEntry) @@ -2573,14 +2600,19 @@ static void extractOneFormalParameterVar(FunctionEntry* f, varPtr->location_expression_size = paramPtr->dwarf_stack_size; } - varPtr->validLoc = paramPtr->valid_loc; - 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); + } else if (isSupportedRegisterLocation(paramPtr, varPtr)) { + UInt regNum = paramPtr->dwarf_stack[0].atom - DW_OP_reg0; + varPtr->validLoc = 1; + varPtr->locationType = REGISTER_LOCATION; + f->paramRegMask |= 1U << regNum; + FJALAR_DPRINTF(" location is register %s\n", dwarf_reg_name(regNum)); } FJALAR_DPRINTF("EXIT extractOneFormalParameterVar\n"); diff --git a/valgrind/fjalar/kvasir/kvasir_main.c b/valgrind/fjalar/kvasir/kvasir_main.c index ca17cf79..66a15001 100644 --- a/valgrind/fjalar/kvasir/kvasir_main.c +++ b/valgrind/fjalar/kvasir/kvasir_main.c @@ -814,11 +814,12 @@ void fjalar_tool_handle_function_entrance(FunctionExecutionState* f_state) { UInt b; for (i = 0; i < FJALAR_NUM_DWARF_REGS; i++) { - UInt regTag = 0; - if (dwarf_reg_guest_offset[i] >= 0) { - regTag = *(VG_(get_tag_ptr_for_guest_offset)(currentTID, - dwarf_reg_guest_offset[i])); + UInt regTag; + if (!(f_state->func->paramRegMask & (1U << i))) { + continue; } + regTag = *(VG_(get_tag_ptr_for_guest_offset)(currentTID, + dwarf_reg_guest_offset(i))); for (b = 0; b < sizeof(Addr); b++) { set_tag((Addr)(&(f_state->entryRegs[i])) + (Addr)b, regTag); } diff --git a/valgrind/fjalar/tests/register-params-test.sh b/valgrind/fjalar/tests/register-params-test.sh index 2af3a35d..b3929aed 100755 --- a/valgrind/fjalar/tests/register-params-test.sh +++ b/valgrind/fjalar/tests/register-params-test.sh @@ -23,11 +23,26 @@ 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, because some versions of Fjalar -# misread them. +# pairs out of the .debug_loc section. gcc -gdwarf-4 -gno-variable-location-views -no-pie -O1 \ -o "${output_dir}/register-params" "${test_dir}/register-params.c" +# register-params.goal assumes that the location of every formal parameter is +# a register throughout its function. Another compiler version might instead +# use a location list, DW_OP_entry_value, or a constant, for which Fjalar omits +# the formal parameter. +if readelf --debug-dump=info "${output_dir}/register-params" \ + | awk '/DW_TAG_formal_parameter/ { inparam = 1; next } + /DW_TAG_/ { inparam = 0 } + inparam && /DW_AT_location/ && !/: [0-9]+ byte block: .*\(DW_OP_reg/ { bad = 1 } + inparam && /DW_AT_const_value/ { bad = 1 } + END { exit !bad }'; then + echo "$0: SKIPPED: gcc did not put every formal parameter in a register" >&2 + readelf --debug-dump=info "${output_dir}/register-params" \ + | grep -A8 DW_TAG_formal_parameter >&2 + exit 0 +fi + decls="${output_dir}/register-params.decls" dtrace="${output_dir}/register-params.dtrace" status=0 @@ -42,19 +57,23 @@ if [ "${status}" -ne 0 ]; then exit 1 fi -# One line per variable at each program point of the invocations with nonces 1 -# and 2 (the first calls to add and scale): " ", -# followed by one line per variable at each exit program point other than -# main's: " comparability ". +# 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 at +# each exit program point other than main's: " comparability +# ". actual="${output_dir}/register-params.actual" -awk '/^\.\.[a-z]*\(\):::/ { ppt = $0; getline; getline; nonce = $0; next } +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 (nonce == 1 || nonce == 2) print ppt, var, value }' \ + if (var !~ /^::/) print ppt, var, value }' \ "${dtrace}" > "${actual}" awk '/^ppt / { ppt = $2 } /^ variable / { var = $2 } + var ~ /^::/ { next } /^ comparability / { if (ppt ~ /:::EXIT/ && ppt !~ /^\.\.main\(/) print ppt, var, "comparability", $2 }' \ "${decls}" >> "${actual}" diff --git a/valgrind/fjalar/tests/register-params.c b/valgrind/fjalar/tests/register-params.c index 59efcf7c..6c55259c 100644 --- a/valgrind/fjalar/tests/register-params.c +++ b/valgrind/fjalar/tests/register-params.c @@ -25,11 +25,31 @@ __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. +__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. +__attribute__((noinline)) double identity(double d) { + return d; +} + 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); + t += (int)identity(1.5); return t == 12345; } diff --git a/valgrind/fjalar/tests/register-params.goal b/valgrind/fjalar/tests/register-params.goal index b42a1263..72812e47 100644 --- a/valgrind/fjalar/tests/register-params.goal +++ b/valgrind/fjalar/tests/register-params.goal @@ -8,9 +8,15 @@ ..scale():::EXIT0 x 1 ..scale():::EXIT0 factor 7 ..scale():::EXIT0 return 14 -..scale():::EXIT0 x comparability 1 -..scale():::EXIT0 factor comparability 2 -..scale():::EXIT0 return comparability 2 -..add():::EXIT0 a comparability 1 -..add():::EXIT0 b comparability 1 -..add():::EXIT0 return comparability 1 +..identity():::EXIT0 return 1.5 +..identity():::EXIT0 d comparability 2 +..identity():::EXIT0 return comparability 3 +..split():::EXIT0 p comparability 2 +..split():::EXIT0 p.first comparability 3 +..split():::EXIT0 p.second comparability 4 +..scale():::EXIT0 x comparability 2 +..scale():::EXIT0 factor comparability 3 +..scale():::EXIT0 return comparability 3 +..add():::EXIT0 a comparability 2 +..add():::EXIT0 b comparability 2 +..add():::EXIT0 return comparability 2 diff --git a/valgrind/fjalar/typedata.c b/valgrind/fjalar/typedata.c index b96e81ee..a8144d0a 100644 --- a/valgrind/fjalar/typedata.c +++ b/valgrind/fjalar/typedata.c @@ -1234,6 +1234,20 @@ char harvest_formal_param_location_offset(dwarf_entry* e, long value) return 0; } +char harvest_formal_param_location_piece(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_piece = 1; + return 1; + } + else + return 0; +} + char harvest_data_member_location(dwarf_entry* e, unsigned long value) { unsigned long tag; @@ -1914,6 +1928,7 @@ 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_piece = cur_param->location_has_piece; 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..3c83e219 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -347,6 +347,10 @@ typedef struct // way to get the parameter location unsigned int valid_loc; + // True if the location expression contains DW_OP_piece, which + // dwarf_stack does not record. + char location_has_piece; + unsigned long abstract_origin_ID; // See comment in the function struct definition // for the uses of this. @@ -568,6 +572,7 @@ 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_piece(dwarf_entry* e); 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); From 5ffb72a9b8e139d064d34b24d21505ec4d8b4301 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 18:13:04 -0700 Subject: [PATCH 03/23] Address code review of register-parameter tracing * Omit a formal parameter whose location Fjalar cannot read from the .decls file too, so that it matches the .dtrace file. * A register parameter occupies no stack slot when computing stack parameter offsets. * Handle a DW_OP_bregN frame base. * Save at most FJALAR_MAX_PARAM_REGS registers per invocation, listed in FunctionEntry.paramRegs, rather than one slot per DWARF register. * Treat DW_OP_bit_piece like DW_OP_piece. * register-params-test.sh fails rather than skips in CI. * Use binutils' register names for debugging output. --- valgrind/fjalar/dwarf.c | 10 ++ valgrind/fjalar/fjalar_include.h | 41 ++++--- valgrind/fjalar/fjalar_main.c | 100 +++++++++--------- valgrind/fjalar/fjalar_traversal.c | 27 ++--- valgrind/fjalar/generate_fjalar_entries.c | 17 ++- valgrind/fjalar/kvasir/kvasir_main.c | 13 +-- valgrind/fjalar/tests/register-params-test.sh | 9 +- valgrind/fjalar/tests/register-params.goal | 6 +- valgrind/fjalar/typedata.h | 4 +- 9 files changed, 134 insertions(+), 93 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index c286843b..6cf31350 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1869,6 +1869,8 @@ decode_location_expression (unsigned char * data, printf ("DW_OP_call_frame_cfa"); break; case DW_OP_bit_piece: + if (ok_to_harvest) + harvest_formal_param_location_piece(entry); printf ("DW_OP_bit_piece: "); READ_ULEB (uvalue, data, end); printf (_("size: %s "), dwarf_vmatoa ("u", uvalue)); @@ -9656,6 +9658,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_include.h b/valgrind/fjalar/fjalar_include.h index 33873bc9..054340e6 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -558,6 +558,13 @@ VarList globalVars; /******** FunctionEntry ********/ +// The maximum number of distinct registers that hold a function's +// formal parameters and that Fjalar saves at function entrance. +// This is more than the 6 integer argument registers of the amd64 +// ABI. Fjalar omits a formal parameter whose register would exceed +// it. Every FunctionExecutionState has room for this many registers. +#define FJALAR_MAX_PARAM_REGS 8 + // Contains information about a particular function - // Should be IMMUTABLE after initialization typedef struct _FunctionEntry { @@ -690,9 +697,11 @@ typedef struct _FunctionEntry { UInt nonce; - // A bit mask of the DWARF register numbers of the registers that - // hold formal parameters (see FunctionExecutionState.entryRegs). - UInt paramRegMask; + // The DWARF register numbers of the registers that hold formal + // parameters. At function entrance, Fjalar saves register + // paramRegs[k] in FunctionExecutionState.entryRegs[k]. + UChar paramRegs[FJALAR_MAX_PARAM_REGS]; + UInt numParamRegs; } FunctionEntry; @@ -745,14 +754,18 @@ traversals within data structures and arrays **********************************************************************/ // One more than the largest DWARF register number that Fjalar can -// read. It must be at most 32, because FunctionEntry.paramRegMask is -// a bit mask indexed by DWARF register number. +// read. #if defined(VGA_amd64) #define FJALAR_NUM_DWARF_REGS 16 #else #define FJALAR_NUM_DWARF_REGS 11 #endif +// Returns the index of the given DWARF register number in +// f->paramRegs (and thus in FunctionExecutionState.entryRegs), or -1 +// if it is not there. +Int param_reg_index(FunctionEntry* f, UInt regNum); + // Returns true if Fjalar can read the register with the given DWARF // register number. Bool dwarf_reg_is_readable(UInt regNum); @@ -766,7 +779,7 @@ Int dwarf_reg_guest_offset(UInt regNum); Addr read_dwarf_reg(ThreadId tid, UInt regNum); // Returns the name of the register with the given DWARF register -// number, for debugging output. +// 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 @@ -820,14 +833,14 @@ typedef struct { int virtualStackByteSize; // Number of 1-byte entries in virtualStack int virtualStackFPOffset; // Where in the stack the frame pointer was - // The values of the registers at function entrance, indexed by - // DWARF register number. A formal parameter whose location is a - // register (DW_OP_reg*) is read from here, at both entrance and - // exit, for the same reason as virtualStack. At function entrance, - // Fjalar copies the registers in func->paramRegMask, with their A - // and V bits, to entryRegs, and DynComp copies their tags. The - // other elements are not set. - Addr entryRegs[FJALAR_NUM_DWARF_REGS]; + // The values at function entrance of the registers in + // func->paramRegs: entryRegs[k] holds register func->paramRegs[k]. + // A formal parameter whose location is a register (DW_OP_reg*) is + // read from here, at both entrance and exit, for the same reason as + // virtualStack. At function entrance, Fjalar copies the registers, + // with their A and V bits, to entryRegs, and DynComp copies their + // tags. The other elements are not set. + Addr entryRegs[FJALAR_MAX_PARAM_REGS]; Addr lowSP; diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index c493b090..2c95aeac 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -96,7 +96,7 @@ Bool doing_debug_print = False; const HChar* executable_filename = 0; // Mapping between DWARF register numbers and the guest-state offset -// and name of the register. +// of the register, or -1 if Fjalar cannot read the register. // Below comment is ripped from GCC 4.3.1 /* Define the register numbers to be used in Dwarf debugging information. @@ -119,57 +119,52 @@ const HChar* executable_filename = 0; */ -typedef struct { - Int guestOffset; // -1 if Fjalar cannot read the register - const HChar* name; // for debugging output -} DwarfRegInfo; - #if defined(VGA_amd64) // AMD64 Dwarf to Architecture mapping is (thankfully) specified // in the AMD64 ABI (http://x86-64.org/documentation/abi.pdf) -static const DwarfRegInfo dwarf_regs[FJALAR_NUM_DWARF_REGS] = { - { offsetof(VexGuestArchState, guest_RAX), "xAX" }, - { offsetof(VexGuestArchState, guest_RDX), "xDX" }, - { offsetof(VexGuestArchState, guest_RCX), "xCX" }, - { offsetof(VexGuestArchState, guest_RBX), "xBX" }, - { offsetof(VexGuestArchState, guest_RSI), "xSI" }, - { offsetof(VexGuestArchState, guest_RDI), "xDI" }, - { offsetof(VexGuestArchState, guest_RBP), "xFP" }, - { offsetof(VexGuestArchState, guest_RSP), "xSP" }, - { offsetof(VexGuestArchState, guest_R8), "R8" }, - { offsetof(VexGuestArchState, guest_R9), "R9" }, - { offsetof(VexGuestArchState, guest_R10), "R10" }, - { offsetof(VexGuestArchState, guest_R11), "R11" }, - { offsetof(VexGuestArchState, guest_R12), "R12" }, - { offsetof(VexGuestArchState, guest_R13), "R13" }, - { offsetof(VexGuestArchState, guest_R14), "R14" }, - { offsetof(VexGuestArchState, guest_R15), "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 -static const DwarfRegInfo dwarf_regs[FJALAR_NUM_DWARF_REGS] = { - { offsetof(VexGuestArchState, guest_EAX), "xAX" }, - { offsetof(VexGuestArchState, guest_ECX), "xCX" }, - { offsetof(VexGuestArchState, guest_EDX), "xDX" }, - { offsetof(VexGuestArchState, guest_EBX), "xBX" }, - { offsetof(VexGuestArchState, guest_ESP), "xSP" }, - { offsetof(VexGuestArchState, guest_EBP), "xFP" }, - { offsetof(VexGuestArchState, guest_ESI), "xSI" }, - { offsetof(VexGuestArchState, guest_EDI), "xDI" }, - { offsetof(VexGuestArchState, guest_EIP), "xIP" }, - { -1, "eflags" }, - { -1, "trapno" }, +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), + -1, // eflags + -1, // trapno }; #endif Bool dwarf_reg_is_readable(UInt regNum) { - return regNum < FJALAR_NUM_DWARF_REGS && dwarf_regs[regNum].guestOffset >= 0; + return regNum < FJALAR_NUM_DWARF_REGS && dwarf_reg_guest_offsets[regNum] >= 0; } Int dwarf_reg_guest_offset(UInt regNum) { tl_assert(dwarf_reg_is_readable(regNum)); - return dwarf_regs[regNum].guestOffset; + return dwarf_reg_guest_offsets[regNum]; } Addr read_dwarf_reg(ThreadId tid, UInt regNum) @@ -180,9 +175,15 @@ Addr read_dwarf_reg(ThreadId tid, UInt regNum) return value; } -const HChar* dwarf_reg_name(UInt regNum) +Int param_reg_index(FunctionEntry* f, UInt regNum) { - return regNum < FJALAR_NUM_DWARF_REGS ? dwarf_regs[regNum].name : "unknown"; + UInt k; + for (k = 0; k < f->numParamRegs; k++) { + if (f->paramRegs[k] == regNum) { + return k; + } + } + return -1; } // located in VEX/priv/main_util.c @@ -544,7 +545,7 @@ void enter_function(FunctionEntry* f) { FunctionExecutionState* newEntry; extern FunctionExecutionState* curFunctionExecutionStatePtr; - int i; + UInt i; // Only do enter_function if this is the first time we have // reached the preferred entry point after entering the function. @@ -649,9 +650,14 @@ void enter_function(FunctionEntry* f) df = df->next; } } else { + // The frame base is DW_OP_regN (whose offset is 0) or DW_OP_bregN + // plus an offset. + UInt regNum = ((f->frame_base_atom >= DW_OP_breg0 && f->frame_base_atom <= DW_OP_breg31) + ? f->frame_base_atom - DW_OP_breg0 + : f->frame_base_atom - DW_OP_reg0); FJALAR_DPRINTF("\tsimple location expression\n"); - if (dwarf_reg_is_readable(f->frame_base_atom - DW_OP_reg0)) { - frame_ptr = read_dwarf_reg(tid, f->frame_base_atom - DW_OP_reg0) + f->frame_base_offset; + if (dwarf_reg_is_readable(regNum)) { + frame_ptr = read_dwarf_reg(tid, regNum) + f->frame_base_offset; } } } @@ -690,16 +696,14 @@ void enter_function(FunctionEntry* f) // Save the registers that hold formal parameters, with their A and // V bits, so that those formal parameters have their entrance values // at both entrance and exit. - for (i = 0; i < FJALAR_NUM_DWARF_REGS; i++) { + for (i = 0; i < f->numParamRegs; i++) { + UInt regNum = f->paramRegs[i]; Addr regAddr = (Addr)&newEntry->entryRegs[i]; UWord vbits; UInt b; - if (!(f->paramRegMask & (1U << i))) { - continue; - } - newEntry->entryRegs[i] = read_dwarf_reg(tid, i); + newEntry->entryRegs[i] = read_dwarf_reg(tid, regNum); VG_(get_shadow_regs_area)(tid, (UChar*)&vbits, 1/*shadowNo*/, - dwarf_reg_guest_offset(i), sizeof(Addr)); + dwarf_reg_guest_offset(regNum), sizeof(Addr)); for (b = 0; b < sizeof(Addr); b++) { set_abit_and_vbyte(regAddr + b, VGM_BIT_VALID, (vbits >> (b * 8)) & 0xff); } diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index 92f06c07..b186264a 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -933,18 +933,20 @@ void visitVariableGroup(VariableOrigin varOrigin, 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) { 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; - } - 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))); @@ -1037,10 +1039,11 @@ void visitVariableGroup(VariableOrigin varOrigin, // function entrance, which is saved (with its A and V // bits and tags) in the FunctionExecutionState. unsigned int regNum = op - DW_OP_reg0; + Int regIndex = param_reg_index(funcPtr, regNum); FJALAR_DPRINTF("\tObtaining entrance value of register: [%%%s]\n", dwarf_reg_name(regNum)); - if (!(funcPtr->paramRegMask & (1U << regNum))) { + if (regIndex < 0) { // extractOneFormalParameterVar() did not record the - // register in paramRegMask, so it was not saved, but + // register in paramRegs, so it was not saved, but // some heuristic marked the location valid anyway. FJALAR_DPRINTF("\tRegister %s was not saved at entrance; value is nonsensical\n", dwarf_reg_name(regNum)); @@ -1051,7 +1054,7 @@ void visitVariableGroup(VariableOrigin varOrigin, // the entrance or exit of the current invocation. tl_assert(curFunctionExecutionStatePtr && curFunctionExecutionStatePtr->func == funcPtr); - var_loc = (Addr)&curFunctionExecutionStatePtr->entryRegs[regNum]; + var_loc = (Addr)&curFunctionExecutionStatePtr->entryRegs[regIndex]; } else if((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { // Get value pointed to by architectural register diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index 370d43a2..a428c892 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2403,8 +2403,11 @@ static void verifyStackParamWordAlignment(FunctionEntry* f, int replace) cur_node = cur_node->next) { int cur_byteSize = 0; - if (cur_node->var->locationType == NO_LOCATION || - (replace && cur_node->var->locationType != REGISTER_LOCATION)) { + // A parameter in a register occupies no stack slot. + if (cur_node->var->locationType == REGISTER_LOCATION) { + 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], cur_node->var->varType->byteSize); @@ -2609,9 +2612,17 @@ static void extractOneFormalParameterVar(FunctionEntry* f, 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; + if (param_reg_index(f, regNum) < 0) { + if (f->numParamRegs == FJALAR_MAX_PARAM_REGS) { + FJALAR_DPRINTF(" location is register %s, but too many registers hold formal parameters\n", + dwarf_reg_name(regNum)); + FJALAR_DPRINTF("EXIT extractOneFormalParameterVar\n"); + return; + } + f->paramRegs[f->numParamRegs++] = regNum; + } varPtr->validLoc = 1; varPtr->locationType = REGISTER_LOCATION; - f->paramRegMask |= 1U << regNum; FJALAR_DPRINTF(" location is register %s\n", dwarf_reg_name(regNum)); } diff --git a/valgrind/fjalar/kvasir/kvasir_main.c b/valgrind/fjalar/kvasir/kvasir_main.c index 66a15001..cbd47cf7 100644 --- a/valgrind/fjalar/kvasir/kvasir_main.c +++ b/valgrind/fjalar/kvasir/kvasir_main.c @@ -810,16 +810,13 @@ void fjalar_tool_handle_function_entrance(FunctionExecutionState* f_state) { ThreadId currentTID = VG_(get_running_tid)(); // For DynComp, set the tags of the saved register values - int i; + UInt i; UInt b; - for (i = 0; i < FJALAR_NUM_DWARF_REGS; i++) { - UInt regTag; - if (!(f_state->func->paramRegMask & (1U << i))) { - continue; - } - regTag = *(VG_(get_tag_ptr_for_guest_offset)(currentTID, - dwarf_reg_guest_offset(i))); + for (i = 0; i < f_state->func->numParamRegs; i++) { + UInt regNum = f_state->func->paramRegs[i]; + UInt regTag = *(VG_(get_tag_ptr_for_guest_offset)(currentTID, + dwarf_reg_guest_offset(regNum))); for (b = 0; b < sizeof(Addr); b++) { set_tag((Addr)(&(f_state->entryRegs[i])) + (Addr)b, regTag); } diff --git a/valgrind/fjalar/tests/register-params-test.sh b/valgrind/fjalar/tests/register-params-test.sh index b3929aed..1cfc5232 100755 --- a/valgrind/fjalar/tests/register-params-test.sh +++ b/valgrind/fjalar/tests/register-params-test.sh @@ -37,9 +37,16 @@ if readelf --debug-dump=info "${output_dir}/register-params" \ inparam && /DW_AT_location/ && !/: [0-9]+ byte block: .*\(DW_OP_reg/ { bad = 1 } inparam && /DW_AT_const_value/ { bad = 1 } END { exit !bad }'; then - echo "$0: SKIPPED: gcc did not put every formal parameter in a register" >&2 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 diff --git a/valgrind/fjalar/tests/register-params.goal b/valgrind/fjalar/tests/register-params.goal index 72812e47..465c2be7 100644 --- a/valgrind/fjalar/tests/register-params.goal +++ b/valgrind/fjalar/tests/register-params.goal @@ -9,11 +9,7 @@ ..scale():::EXIT0 factor 7 ..scale():::EXIT0 return 14 ..identity():::EXIT0 return 1.5 -..identity():::EXIT0 d comparability 2 -..identity():::EXIT0 return comparability 3 -..split():::EXIT0 p comparability 2 -..split():::EXIT0 p.first comparability 3 -..split():::EXIT0 p.second comparability 4 +..identity():::EXIT0 return comparability 2 ..scale():::EXIT0 x comparability 2 ..scale():::EXIT0 factor comparability 3 ..scale():::EXIT0 return comparability 3 diff --git a/valgrind/fjalar/typedata.h b/valgrind/fjalar/typedata.h index 3c83e219..1a809e29 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -347,8 +347,8 @@ typedef struct // way to get the parameter location unsigned int valid_loc; - // True if the location expression contains DW_OP_piece, which - // dwarf_stack does not record. + // True if the location expression contains DW_OP_piece or + // DW_OP_bit_piece, which dwarf_stack does not record. char location_has_piece; unsigned long abstract_origin_ID; // See comment in the function struct definition From 5df375cfa46c3a5a7a673ae23b95fed29cb6c9cf Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 18:26:58 -0700 Subject: [PATCH 04/23] Address code review of register-parameter tracing - Compute the frame base from a DW_OP_regN location-list entry, not just DW_OP_bregN. - Use the architecture's frame-pointer register in the DW_OP_deref frame-base workaround. - Skip register-params-test.sh when a formal parameter has no location. - Store entryRegs in the virtualStack allocation rather than in every FunctionExecutionState. - Initialize DWARF register names, so debugging output names registers. - Omit a formal parameter whose location contains any operation that Fjalar does not record. - Pass entryRegs to visitVariableGroup rather than reading a global. - Save register values, A and V bits, and DynComp tags in one place. --- valgrind/fjalar/basic-tool/basic-tool.c | 4 + valgrind/fjalar/disambig.c | 3 + valgrind/fjalar/dwarf.c | 18 ++- valgrind/fjalar/fjalar_include.h | 15 ++- valgrind/fjalar/fjalar_main.c | 103 +++++++++++++----- valgrind/fjalar/fjalar_main.h | 4 + valgrind/fjalar/fjalar_select.c | 3 + valgrind/fjalar/fjalar_traversal.c | 12 +- valgrind/fjalar/generate_fjalar_entries.c | 9 +- valgrind/fjalar/kvasir/decls-output.c | 7 ++ valgrind/fjalar/kvasir/dtrace-output.c | 2 + valgrind/fjalar/kvasir/dyncomp_runtime.c | 3 + valgrind/fjalar/kvasir/kvasir_main.c | 18 --- valgrind/fjalar/readelf.c | 3 + valgrind/fjalar/tests/register-params-test.sh | 13 ++- valgrind/fjalar/typedata.c | 13 ++- valgrind/fjalar/typedata.h | 12 +- 17 files changed, 162 insertions(+), 80 deletions(-) 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 6cf31350..7d17f2d4 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1447,6 +1447,11 @@ decode_location_expression (unsigned char * data, while (data < end) { + /* The size of a formal parameter's dwarf_stack before this op, or + -1 if not harvesting a formal parameter's location. */ + int param_stack_size_before + = (ok_to_harvest ? formal_param_location_stack_size (entry) : -1); + op = *data++; if(ll) {ll->atom = op;} @@ -1809,8 +1814,6 @@ decode_location_expression (unsigned char * data, break; case DW_OP_piece: READ_ULEB (uvalue, data, end); - if (ok_to_harvest) - harvest_formal_param_location_piece(entry); printf ("DW_OP_piece: %s", dwarf_vmatoa ("u", uvalue)); break; case DW_OP_deref_size: @@ -1869,8 +1872,6 @@ decode_location_expression (unsigned char * data, printf ("DW_OP_call_frame_cfa"); break; case DW_OP_bit_piece: - if (ok_to_harvest) - harvest_formal_param_location_piece(entry); printf ("DW_OP_bit_piece: "); READ_ULEB (uvalue, data, end); printf (_("size: %s "), dwarf_vmatoa ("u", uvalue)); @@ -2078,10 +2079,19 @@ decode_location_expression (unsigned char * data, printf (_("(User defined location op 0x%x)"), op); else printf (_("(Unknown location op 0x%x)"), op); + if (param_stack_size_before >= 0) + harvest_formal_param_location_unsupported (entry); /* No way to tell where the next op is, so just bail. */ return need_frame_base; } + /* An op that is not in a formal parameter's dwarf_stack, such as + DW_OP_piece or DW_OP_stack_value, makes the location one that + Fjalar does not support. */ + if (param_stack_size_before >= 0 + && formal_param_location_stack_size (entry) == param_stack_size_before) + harvest_formal_param_location_unsupported (entry); + /* Separate the ops. */ if (data < end) printf ("; "); diff --git a/valgrind/fjalar/fjalar_include.h b/valgrind/fjalar/fjalar_include.h index 054340e6..fede9ee9 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -562,7 +562,7 @@ VarList globalVars; // formal parameters and that Fjalar saves at function entrance. // This is more than the 6 integer argument registers of the amd64 // ABI. Fjalar omits a formal parameter whose register would exceed -// it. Every FunctionExecutionState has room for this many registers. +// it. #define FJALAR_MAX_PARAM_REGS 8 // Contains information about a particular function - @@ -838,9 +838,10 @@ typedef struct { // A formal parameter whose location is a register (DW_OP_reg*) is // read from here, at both entrance and exit, for the same reason as // virtualStack. At function entrance, Fjalar copies the registers, - // with their A and V bits, to entryRegs, and DynComp copies their - // tags. The other elements are not set. - Addr entryRegs[FJALAR_MAX_PARAM_REGS]; + // with their A and V bits and DynComp tags, to entryRegs. entryRegs + // has func->numParamRegs elements and lies in the same allocation as + // virtualStack, just after it; it is null if func->numParamRegs is 0. + Addr* entryRegs; Addr lowSP; @@ -1006,6 +1007,12 @@ void visitVariableGroup(VariableOrigin varOrigin, // address space (it's what we'll dereference) Addr stackBaseAddr, Addr stackBaseAddrGuest, + // The values at function entrance of the + // registers that hold formal parameters, 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 2c95aeac..855e2273 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -175,6 +175,22 @@ Addr read_dwarf_reg(ThreadId tid, UInt regNum) return value; } +// If atom is DW_OP_regN or DW_OP_bregN, sets *regNum to N and returns +// True. Otherwise, returns False. A frame base of DW_OP_regN has +// offset 0, and one of DW_OP_bregN has the atom's offset. +static Bool frame_base_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; +} + Int param_reg_index(FunctionEntry* f, UInt regNum) { UInt k; @@ -186,6 +202,35 @@ Int param_reg_index(FunctionEntry* f, UInt regNum) return -1; } +// Saves the registers that hold formal parameters of f_state->func, +// with their A and V bits and DynComp tags, in f_state->entryRegs, so +// that those formal parameters have their entrance values at both +// entrance and exit. +static void save_param_regs(FunctionExecutionState* f_state, ThreadId tid) +{ + FunctionEntry* f = f_state->func; + UInt i; + for (i = 0; i < f->numParamRegs; i++) { + UInt regNum = f->paramRegs[i]; + Int guestOffset = dwarf_reg_guest_offset(regNum); + Addr regAddr = (Addr)&f_state->entryRegs[i]; + UWord vbits; + UInt b; + f_state->entryRegs[i] = read_dwarf_reg(tid, regNum); + VG_(get_shadow_regs_area)(tid, (UChar*)&vbits, 1/*shadowNo*/, + guestOffset, sizeof(Addr)); + for (b = 0; b < sizeof(Addr); b++) { + set_abit_and_vbyte(regAddr + b, VGM_BIT_VALID, (vbits >> (b * 8)) & 0xff); + } + if (kvasir_with_dyncomp) { + UInt regTag = *VG_(get_tag_ptr_for_guest_offset)(tid, guestOffset); + for (b = 0; b < sizeof(Addr); b++) { + set_tag(regAddr + b, regTag); + } + } + } +} + // located in VEX/priv/main_util.c extern void vex_bzero(void* s, UInt n); // located in my_libc.c @@ -545,7 +590,6 @@ void enter_function(FunctionEntry* f) { FunctionExecutionState* newEntry; extern FunctionExecutionState* curFunctionExecutionStatePtr; - UInt i; // Only do enter_function if this is the first time we have // reached the preferred entry point after entering the function. @@ -558,6 +602,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); @@ -624,13 +669,18 @@ 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(dwarf_reg_is_readable(ll->atom - DW_OP_breg0)) { - frame_ptr = read_dwarf_reg(tid, ll->atom - DW_OP_breg0) + ll->atom_offset; + if (frame_base_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)); } } } @@ -650,14 +700,12 @@ void enter_function(FunctionEntry* f) df = df->next; } } else { - // The frame base is DW_OP_regN (whose offset is 0) or DW_OP_bregN - // plus an offset. - UInt regNum = ((f->frame_base_atom >= DW_OP_breg0 && f->frame_base_atom <= DW_OP_breg31) - ? f->frame_base_atom - DW_OP_breg0 - : f->frame_base_atom - DW_OP_reg0); FJALAR_DPRINTF("\tsimple location expression\n"); - if (dwarf_reg_is_readable(regNum)) { + if (frame_base_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)); } } } @@ -693,22 +741,6 @@ void enter_function(FunctionEntry* f) newEntry->invocation_nonce = cur_nonce++; newEntry->func->nonce = newEntry->invocation_nonce; - // Save the registers that hold formal parameters, with their A and - // V bits, so that those formal parameters have their entrance values - // at both entrance and exit. - for (i = 0; i < f->numParamRegs; i++) { - UInt regNum = f->paramRegs[i]; - Addr regAddr = (Addr)&newEntry->entryRegs[i]; - UWord vbits; - UInt b; - newEntry->entryRegs[i] = read_dwarf_reg(tid, regNum); - VG_(get_shadow_regs_area)(tid, (UChar*)&vbits, 1/*shadowNo*/, - dwarf_reg_guest_offset(regNum), sizeof(Addr)); - for (b = 0; b < sizeof(Addr); b++) { - set_abit_and_vbyte(regAddr + b, VGM_BIT_VALID, (vbits >> (b * 8)) & 0xff); - } - } - // FJALAR VIRTUAL STACK // Fjalar maintains a virtual stack for invocation a function. This // allows Fjalar to provide tools with unaltered values of formal @@ -742,7 +774,14 @@ 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)); + newEntry->virtualStack = VG_(calloc)("fjalar_main.c: enter_func", + entryRegsOffset + f->numParamRegs * sizeof(Addr), + sizeof(char)); + newEntry->entryRegs = (f->numParamRegs == 0 + ? NULL + : (Addr*)(newEntry->virtualStack + entryRegsOffset)); newEntry->virtualStackByteSize = size; newEntry->virtualStackFPOffset = local_stack; @@ -767,6 +806,7 @@ void enter_function(FunctionEntry* f) newEntry->func->guestStackEnd = newEntry->func->guestStackStart + size; newEntry->func->lowestVirtSP = (Addr)newEntry->virtualStack; + save_param_regs(newEntry, tid); } else { printf("Obtained a stack size of 0 for Function: %s. Aborting\n", f->fjalar_name); @@ -942,7 +982,10 @@ 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); + Addr allocEnd = (top->entryRegs + ? (Addr)(top->entryRegs + top->func->numParamRegs) + : (Addr)top->virtualStack + top->virtualStackByteSize); + mc_make_noaccess((Addr)top->virtualStack, allocEnd - (Addr)top->virtualStack); VG_(free)(top->virtualStack); } diff --git a/valgrind/fjalar/fjalar_main.h b/valgrind/fjalar/fjalar_main.h index 40c6bf8a..7f597a90 100644 --- a/valgrind/fjalar/fjalar_main.h +++ b/valgrind/fjalar/fjalar_main.h @@ -188,9 +188,13 @@ char* fjalar_program_stderr_filename; #if defined(VGA_amd64) // The DWARF register number of the stack pointer #define DWARF_SP_REG 7 +// The DWARF register number of the frame pointer +#define DWARF_FP_REG 6 #else // The DWARF register number of the stack pointer #define DWARF_SP_REG 4 +// The DWARF register number of the frame pointer +#define DWARF_FP_REG 5 #endif 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 b186264a..ee2bbbbf 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -30,10 +30,6 @@ #include "pub_tool_threadstate.h" -// The state of the function invocation whose entrance or exit is -// being processed (defined in fjalar_runtime.c) -extern FunctionExecutionState* curFunctionExecutionStatePtr; - // This increments every time a call to visitSingleVar() or // visitSequence() is made. It is up to the caller to reset this // properly! @@ -868,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) { @@ -1050,11 +1047,8 @@ void visitVariableGroup(VariableOrigin varOrigin, var_loc = 0; break; } - // Every caller that supplies stackBaseAddr is processing - // the entrance or exit of the current invocation. - tl_assert(curFunctionExecutionStatePtr && - curFunctionExecutionStatePtr->func == funcPtr); - var_loc = (Addr)&curFunctionExecutionStatePtr->entryRegs[regIndex]; + tl_assert(entryRegs); + var_loc = (Addr)&entryRegs[regIndex]; } else if((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { // Get value pointed to by architectural register diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index a428c892..3cf3eb95 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2524,14 +2524,14 @@ int determineFormalParametersLowerStackByteSize(FunctionEntry* f) // 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, nor a value split into pieces. +// 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 || paramPtr->location_has_piece) { + if (paramPtr->dwarf_stack_size != 1) { return False; } atom = paramPtr->dwarf_stack[0].atom; @@ -2603,7 +2603,10 @@ static void extractOneFormalParameterVar(FunctionEntry* f, varPtr->location_expression_size = paramPtr->dwarf_stack_size; } - if (paramPtr->location_type == LT_FP_OFFSET) { + if (paramPtr->location_has_unsupported_op) { + // varPtr->validLoc remains 0, so Fjalar omits the formal parameter. + FJALAR_DPRINTF(" location contains an unsupported operation\n"); + } else if (paramPtr->location_type == LT_FP_OFFSET) { varPtr->validLoc = paramPtr->valid_loc; varPtr->locationType = FP_OFFSET_LOCATION; varPtr->byteOffset = paramPtr->location; 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/kvasir/kvasir_main.c b/valgrind/fjalar/kvasir/kvasir_main.c index cbd47cf7..b0b7d538 100644 --- a/valgrind/fjalar/kvasir/kvasir_main.c +++ b/valgrind/fjalar/kvasir/kvasir_main.c @@ -805,24 +805,6 @@ void fjalar_tool_handle_function_entrance(FunctionExecutionState* f_state) { kvasir_late_init(); kvasir_late_init_done = True; } - - if (kvasir_with_dyncomp) { - ThreadId currentTID = VG_(get_running_tid)(); - - // For DynComp, set the tags of the saved register values - UInt i; - UInt b; - - for (i = 0; i < f_state->func->numParamRegs; i++) { - UInt regNum = f_state->func->paramRegs[i]; - UInt regTag = *(VG_(get_tag_ptr_for_guest_offset)(currentTID, - dwarf_reg_guest_offset(regNum))); - for (b = 0; b < sizeof(Addr); b++) { - set_tag((Addr)(&(f_state->entryRegs[i])) + (Addr)b, regTag); - } - } - } - printDtraceForFunction(f_state, 1); } diff --git a/valgrind/fjalar/readelf.c b/valgrind/fjalar/readelf.c index 723d202b..3a8ea721 100644 --- a/valgrind/fjalar/readelf.c +++ b/valgrind/fjalar/readelf.c @@ -22611,6 +22611,9 @@ fjalar_process_file (const char * file_name) return FALSE; } + // Register names in debugging output, such as dwarf_reg_name(). + init_dwarf_regnames_by_elf_machine_code (filedata->file_header.e_machine); + // FJALAR_DPRINTF("before process_section_headers()\n"); if (! process_section_headers (filedata)) diff --git a/valgrind/fjalar/tests/register-params-test.sh b/valgrind/fjalar/tests/register-params-test.sh index 1cfc5232..8160f170 100755 --- a/valgrind/fjalar/tests/register-params-test.sh +++ b/valgrind/fjalar/tests/register-params-test.sh @@ -29,14 +29,17 @@ gcc -gdwarf-4 -gno-variable-location-views -no-pie -O1 \ # register-params.goal assumes that the location of every formal parameter is # a register throughout its function. Another compiler version might instead -# use a location list, DW_OP_entry_value, or a constant, for which Fjalar omits -# the formal parameter. +# 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 '/DW_TAG_formal_parameter/ { inparam = 1; next } - /DW_TAG_/ { inparam = 0 } + | awk 'function end_param() { if (inparam && !hasloc) bad = 1; inparam = 0 } + /^ *<[0-9]+><[0-9a-f]+>:/ { end_param() } + /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 { exit !bad }'; then + 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 diff --git a/valgrind/fjalar/typedata.c b/valgrind/fjalar/typedata.c index a8144d0a..d5fade8f 100644 --- a/valgrind/fjalar/typedata.c +++ b/valgrind/fjalar/typedata.c @@ -1234,14 +1234,21 @@ char harvest_formal_param_location_offset(dwarf_entry* e, long value) return 0; } -char harvest_formal_param_location_piece(dwarf_entry* e) +int formal_param_location_stack_size(dwarf_entry* e) +{ + if ((e == 0) || (e->entry_ptr == 0) || !tag_is_formal_parameter(e->tag_name)) + return -1; + return ((formal_parameter*)e->entry_ptr)->dwarf_stack_size; +} + +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_piece = 1; + ((formal_parameter*)e->entry_ptr)->location_has_unsupported_op = 1; return 1; } else @@ -1928,7 +1935,7 @@ 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_piece = cur_param->location_has_piece; + aliased_formal_param->location_has_unsupported_op = cur_param->location_has_unsupported_op; 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 1a809e29..3314ed00 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -347,9 +347,10 @@ typedef struct // way to get the parameter location unsigned int valid_loc; - // True if the location expression contains DW_OP_piece or - // DW_OP_bit_piece, which dwarf_stack does not record. - char location_has_piece; + // True if the location expression contains an operation that + // dwarf_stack does not record, such as DW_OP_piece or + // DW_OP_stack_value. Fjalar cannot read such a location. + char location_has_unsupported_op; unsigned long abstract_origin_ID; // See comment in the function struct definition // for the uses of this. @@ -572,7 +573,10 @@ 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_piece(dwarf_entry* e); +// Returns the size of the dwarf_stack of e, or -1 if e is not a +// formal parameter. +int formal_param_location_stack_size(dwarf_entry* e); +char harvest_formal_param_location_unsupported(dwarf_entry* e); 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); From 63376802bea4f2b4834881196efe48bcf99df3c5 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 18:57:07 -0700 Subject: [PATCH 05/23] Index saved entrance registers by DWARF register number --- valgrind/fjalar/fjalar_include.h | 44 +++++++------------ valgrind/fjalar/fjalar_main.c | 51 ++++++++++------------- valgrind/fjalar/fjalar_traversal.c | 12 +++--- valgrind/fjalar/generate_fjalar_entries.c | 10 +---- 4 files changed, 45 insertions(+), 72 deletions(-) diff --git a/valgrind/fjalar/fjalar_include.h b/valgrind/fjalar/fjalar_include.h index fede9ee9..22db2397 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -558,13 +558,6 @@ VarList globalVars; /******** FunctionEntry ********/ -// The maximum number of distinct registers that hold a function's -// formal parameters and that Fjalar saves at function entrance. -// This is more than the 6 integer argument registers of the amd64 -// ABI. Fjalar omits a formal parameter whose register would exceed -// it. -#define FJALAR_MAX_PARAM_REGS 8 - // Contains information about a particular function - // Should be IMMUTABLE after initialization typedef struct _FunctionEntry { @@ -697,11 +690,10 @@ typedef struct _FunctionEntry { UInt nonce; - // The DWARF register numbers of the registers that hold formal - // parameters. At function entrance, Fjalar saves register - // paramRegs[k] in FunctionExecutionState.entryRegs[k]. - UChar paramRegs[FJALAR_MAX_PARAM_REGS]; - UInt numParamRegs; + // True if some formal parameter's location is a register. If so, + // Fjalar saves the registers at function entrance in + // FunctionExecutionState.entryRegs. + Bool hasRegisterParams; } FunctionEntry; @@ -761,11 +753,6 @@ traversals within data structures and arrays #define FJALAR_NUM_DWARF_REGS 11 #endif -// Returns the index of the given DWARF register number in -// f->paramRegs (and thus in FunctionExecutionState.entryRegs), or -1 -// if it is not there. -Int param_reg_index(FunctionEntry* f, UInt regNum); - // Returns true if Fjalar can read the register with the given DWARF // register number. Bool dwarf_reg_is_readable(UInt regNum); @@ -833,14 +820,15 @@ typedef struct { int virtualStackByteSize; // Number of 1-byte entries in virtualStack int virtualStackFPOffset; // Where in the stack the frame pointer was - // The values at function entrance of the registers in - // func->paramRegs: entryRegs[k] holds register func->paramRegs[k]. - // A formal parameter whose location is a register (DW_OP_reg*) is - // read from here, at both entrance and exit, for the same reason as - // virtualStack. At function entrance, Fjalar copies the registers, - // with their A and V bits and DynComp tags, to entryRegs. entryRegs - // has func->numParamRegs elements and lies in the same allocation as - // virtualStack, just after it; it is null if func->numParamRegs is 0. + // The values at function entrance of the registers, indexed by + // DWARF register number: entryRegs[N] holds register N, if + // dwarf_reg_is_readable(N). A formal parameter whose location is a + // register (DW_OP_reg*) is read from here, at both entrance and + // exit, for the same reason as virtualStack. At function entrance, + // Fjalar copies the readable 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->hasRegisterParams is false. Addr* entryRegs; @@ -1007,9 +995,9 @@ void visitVariableGroup(VariableOrigin varOrigin, // address space (it's what we'll dereference) Addr stackBaseAddr, Addr stackBaseAddrGuest, - // The values at function entrance of the - // registers that hold formal parameters, which - // is FunctionExecutionState.entryRegs (only + // 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, diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index 855e2273..1a2897f4 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -191,32 +191,22 @@ static Bool frame_base_reg_num(UInt atom, UInt* regNum) return False; } -Int param_reg_index(FunctionEntry* f, UInt regNum) -{ - UInt k; - for (k = 0; k < f->numParamRegs; k++) { - if (f->paramRegs[k] == regNum) { - return k; - } - } - return -1; -} - -// Saves the registers that hold formal parameters of f_state->func, -// with their A and V bits and DynComp tags, in f_state->entryRegs, so -// that those formal parameters have their entrance values at both -// entrance and exit. +// Saves the readable registers, with their A and V bits and DynComp +// tags, in f_state->entryRegs, so that formal parameters located in +// registers have their entrance values at both entrance and exit. static void save_param_regs(FunctionExecutionState* f_state, ThreadId tid) { - FunctionEntry* f = f_state->func; - UInt i; - for (i = 0; i < f->numParamRegs; i++) { - UInt regNum = f->paramRegs[i]; - Int guestOffset = dwarf_reg_guest_offset(regNum); - Addr regAddr = (Addr)&f_state->entryRegs[i]; + UInt regNum; + for (regNum = 0; regNum < FJALAR_NUM_DWARF_REGS; regNum++) { + Int guestOffset; + Addr regAddr = (Addr)&f_state->entryRegs[regNum]; UWord vbits; UInt b; - f_state->entryRegs[i] = read_dwarf_reg(tid, regNum); + if (!dwarf_reg_is_readable(regNum)) { + continue; + } + guestOffset = dwarf_reg_guest_offset(regNum); + f_state->entryRegs[regNum] = read_dwarf_reg(tid, regNum); VG_(get_shadow_regs_area)(tid, (UChar*)&vbits, 1/*shadowNo*/, guestOffset, sizeof(Addr)); for (b = 0; b < sizeof(Addr); b++) { @@ -776,12 +766,15 @@ void enter_function(FunctionEntry* f) if (size != 0) { // entryRegs follows the virtual stack in the same allocation. Int entryRegsOffset = VG_ROUNDUP(size, sizeof(Addr)); + Int entryRegsSize = (f->hasRegisterParams + ? FJALAR_NUM_DWARF_REGS * sizeof(Addr) + : 0); newEntry->virtualStack = VG_(calloc)("fjalar_main.c: enter_func", - entryRegsOffset + f->numParamRegs * sizeof(Addr), + entryRegsOffset + entryRegsSize, sizeof(char)); - newEntry->entryRegs = (f->numParamRegs == 0 - ? NULL - : (Addr*)(newEntry->virtualStack + entryRegsOffset)); + newEntry->entryRegs = (f->hasRegisterParams + ? (Addr*)(newEntry->virtualStack + entryRegsOffset) + : NULL); newEntry->virtualStackByteSize = size; newEntry->virtualStackFPOffset = local_stack; @@ -806,7 +799,9 @@ void enter_function(FunctionEntry* f) newEntry->func->guestStackEnd = newEntry->func->guestStackStart + size; newEntry->func->lowestVirtSP = (Addr)newEntry->virtualStack; - save_param_regs(newEntry, tid); + if (f->hasRegisterParams) { + save_param_regs(newEntry, tid); + } } else { printf("Obtained a stack size of 0 for Function: %s. Aborting\n", f->fjalar_name); @@ -983,7 +978,7 @@ void exit_function(FunctionEntry* f) make it inaccessible again before allowing Valgrind's malloc to use it, lest assertions fail later. */ Addr allocEnd = (top->entryRegs - ? (Addr)(top->entryRegs + top->func->numParamRegs) + ? (Addr)(top->entryRegs + FJALAR_NUM_DWARF_REGS) : (Addr)top->virtualStack + top->virtualStackByteSize); mc_make_noaccess((Addr)top->virtualStack, allocEnd - (Addr)top->virtualStack); VG_(free)(top->virtualStack); diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index ee2bbbbf..2f5453f2 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -1036,19 +1036,17 @@ void visitVariableGroup(VariableOrigin varOrigin, // function entrance, which is saved (with its A and V // bits and tags) in the FunctionExecutionState. unsigned int regNum = op - DW_OP_reg0; - Int regIndex = param_reg_index(funcPtr, regNum); FJALAR_DPRINTF("\tObtaining entrance value of register: [%%%s]\n", dwarf_reg_name(regNum)); - if (regIndex < 0) { - // extractOneFormalParameterVar() did not record the - // register in paramRegs, so it was not saved, but - // some heuristic marked the location valid anyway. + if (!entryRegs || !dwarf_reg_is_readable(regNum)) { + // extractOneFormalParameterVar() did not accept the + // register location, so the register was not saved, + // but some heuristic marked the location valid anyway. FJALAR_DPRINTF("\tRegister %s was not saved at entrance; value is nonsensical\n", dwarf_reg_name(regNum)); var_loc = 0; break; } - tl_assert(entryRegs); - var_loc = (Addr)&entryRegs[regIndex]; + var_loc = (Addr)&entryRegs[regNum]; } else if((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { // Get value pointed to by architectural register diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index 3cf3eb95..bb81f10f 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2615,15 +2615,7 @@ static void extractOneFormalParameterVar(FunctionEntry* f, 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; - if (param_reg_index(f, regNum) < 0) { - if (f->numParamRegs == FJALAR_MAX_PARAM_REGS) { - FJALAR_DPRINTF(" location is register %s, but too many registers hold formal parameters\n", - dwarf_reg_name(regNum)); - FJALAR_DPRINTF("EXIT extractOneFormalParameterVar\n"); - return; - } - f->paramRegs[f->numParamRegs++] = regNum; - } + f->hasRegisterParams = True; varPtr->validLoc = 1; varPtr->locationType = REGISTER_LOCATION; FJALAR_DPRINTF(" location is register %s\n", dwarf_reg_name(regNum)); From d914a74b8c96599f8f4b289ba162beded7866b24 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 20:56:59 -0700 Subject: [PATCH 06/23] Address code review of register-parameter tracing --- valgrind/fjalar/dwarf.c | 39 +++++++++++++++------ valgrind/fjalar/fjalar_include.h | 20 +++++++---- valgrind/fjalar/fjalar_main.c | 42 +++++++---------------- valgrind/fjalar/fjalar_main.h | 4 --- valgrind/fjalar/fjalar_traversal.c | 31 +++++++++-------- valgrind/fjalar/generate_fjalar_entries.c | 12 ++++++- valgrind/fjalar/mc_include.h | 4 +++ valgrind/fjalar/mc_main.c | 20 +++++++++++ valgrind/fjalar/tests/register-params.c | 4 +-- 9 files changed, 108 insertions(+), 68 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index 7d17f2d4..7c2e7059 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1474,10 +1474,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: @@ -1517,16 +1527,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); @@ -2086,9 +2102,10 @@ decode_location_expression (unsigned char * data, } /* An op that is not in a formal parameter's dwarf_stack, such as - DW_OP_piece or DW_OP_stack_value, makes the location one that - Fjalar does not support. */ - if (param_stack_size_before >= 0 + DW_OP_piece, DW_OP_stack_value, or DW_OP_plus (which Fjalar's + evaluator, having no operand stack, cannot apply), makes the + location one that Fjalar does not support. */ + if (param_stack_size_before >= 0 && op != DW_OP_nop && formal_param_location_stack_size (entry) == param_stack_size_before) harvest_formal_param_location_unsupported (entry); diff --git a/valgrind/fjalar/fjalar_include.h b/valgrind/fjalar/fjalar_include.h index 22db2397..1b6308c1 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -690,10 +690,10 @@ typedef struct _FunctionEntry { UInt nonce; - // True if some formal parameter's location is a register. If so, - // Fjalar saves the registers at function entrance in - // FunctionExecutionState.entryRegs. - Bool hasRegisterParams; + // True if some formal parameter's location expression uses a + // register (DW_OP_reg* or DW_OP_breg*). If so, Fjalar saves the + // registers at function entrance in FunctionExecutionState.entryRegs. + Bool savesEntryRegs; } FunctionEntry; @@ -818,17 +818,23 @@ 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 // dwarf_reg_is_readable(N). A formal parameter whose location is a - // register (DW_OP_reg*) is read from here, at both entrance and - // exit, for the same reason as virtualStack. At function entrance, + // 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 the readable 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->hasRegisterParams is false. + // after it; it is null if func->savesEntryRegs is false. Addr* entryRegs; diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index 1a2897f4..938467e5 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -192,31 +192,17 @@ static Bool frame_base_reg_num(UInt atom, UInt* regNum) } // Saves the readable registers, with their A and V bits and DynComp -// tags, in f_state->entryRegs, so that formal parameters located in -// registers have their entrance values at both entrance and exit. +// 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++) { - Int guestOffset; - Addr regAddr = (Addr)&f_state->entryRegs[regNum]; - UWord vbits; - UInt b; - if (!dwarf_reg_is_readable(regNum)) { - continue; - } - guestOffset = dwarf_reg_guest_offset(regNum); - f_state->entryRegs[regNum] = read_dwarf_reg(tid, regNum); - VG_(get_shadow_regs_area)(tid, (UChar*)&vbits, 1/*shadowNo*/, - guestOffset, sizeof(Addr)); - for (b = 0; b < sizeof(Addr); b++) { - set_abit_and_vbyte(regAddr + b, VGM_BIT_VALID, (vbits >> (b * 8)) & 0xff); - } - if (kvasir_with_dyncomp) { - UInt regTag = *VG_(get_tag_ptr_for_guest_offset)(tid, guestOffset); - for (b = 0; b < sizeof(Addr); b++) { - set_tag(regAddr + b, regTag); - } + if (dwarf_reg_is_readable(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]); } } } @@ -766,13 +752,14 @@ void enter_function(FunctionEntry* f) if (size != 0) { // entryRegs follows the virtual stack in the same allocation. Int entryRegsOffset = VG_ROUNDUP(size, sizeof(Addr)); - Int entryRegsSize = (f->hasRegisterParams + Int entryRegsSize = (f->savesEntryRegs ? FJALAR_NUM_DWARF_REGS * sizeof(Addr) : 0); + newEntry->allocByteSize = entryRegsOffset + entryRegsSize; newEntry->virtualStack = VG_(calloc)("fjalar_main.c: enter_func", - entryRegsOffset + entryRegsSize, + newEntry->allocByteSize, sizeof(char)); - newEntry->entryRegs = (f->hasRegisterParams + newEntry->entryRegs = (f->savesEntryRegs ? (Addr*)(newEntry->virtualStack + entryRegsOffset) : NULL); newEntry->virtualStackByteSize = size; @@ -799,7 +786,7 @@ void enter_function(FunctionEntry* f) newEntry->func->guestStackEnd = newEntry->func->guestStackStart + size; newEntry->func->lowestVirtSP = (Addr)newEntry->virtualStack; - if (f->hasRegisterParams) { + if (f->savesEntryRegs) { save_param_regs(newEntry, tid); } } @@ -977,10 +964,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. */ - Addr allocEnd = (top->entryRegs - ? (Addr)(top->entryRegs + FJALAR_NUM_DWARF_REGS) - : (Addr)top->virtualStack + top->virtualStackByteSize); - mc_make_noaccess((Addr)top->virtualStack, allocEnd - (Addr)top->virtualStack); + 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 7f597a90..89c6bde7 100644 --- a/valgrind/fjalar/fjalar_main.h +++ b/valgrind/fjalar/fjalar_main.h @@ -186,13 +186,9 @@ char* fjalar_program_stdout_filename; char* fjalar_program_stderr_filename; #if defined(VGA_amd64) -// The DWARF register number of the stack pointer -#define DWARF_SP_REG 7 // The DWARF register number of the frame pointer #define DWARF_FP_REG 6 #else -// The DWARF register number of the stack pointer -#define DWARF_SP_REG 4 // The DWARF register number of the frame pointer #define DWARF_FP_REG 5 #endif diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index 2f5453f2..76ce0630 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -942,8 +942,6 @@ void visitVariableGroup(VariableOrigin varOrigin, } if ((varOrigin == FUNCTION_FORMAL_PARAM) && stackBaseAddr) { - ThreadId tid = VG_(get_running_tid)(); - 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))); @@ -1027,7 +1025,7 @@ 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; @@ -1049,22 +1047,20 @@ void visitVariableGroup(VariableOrigin varOrigin, var_loc = (Addr)&entryRegs[regNum]; } else if((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { - // Get value pointed to by architectural register + // Add an offset to the value that the architectural + // register held at function entrance (after the + // prologue). At function exit, the body may have + // overwritten the register and the epilogue has already + // restored or popped it. unsigned int regNum = op - DW_OP_breg0; - if (regNum == 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_reg_is_readable(regNum)) { - reg_val = read_dwarf_reg(tid, regNum); - } else { - FJALAR_DPRINTF("\tCannot read register %s; value is nonsensical\n", + if (!entryRegs || !dwarf_reg_is_readable(regNum)) { + FJALAR_DPRINTF("\tRegister %s was not saved at entrance; value is nonsensical\n", dwarf_reg_name(regNum)); var_loc = 0; break; } - FJALAR_DPRINTF("\tObtaining register value: [%%%s]: %p\n", dwarf_reg_name(regNum), + 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); @@ -1107,8 +1103,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 bb81f10f..a6007971 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2603,6 +2603,17 @@ static void extractOneFormalParameterVar(FunctionEntry* f, varPtr->location_expression_size = paramPtr->dwarf_stack_size; } + { + unsigned int i; + for (i = 0; i < paramPtr->dwarf_stack_size; i++) { + UInt atom = paramPtr->dwarf_stack[i].atom; + if ((atom >= DW_OP_reg0 && atom <= DW_OP_reg31) || + (atom >= DW_OP_breg0 && atom <= DW_OP_breg31)) { + f->savesEntryRegs = True; + } + } + } + if (paramPtr->location_has_unsupported_op) { // varPtr->validLoc remains 0, so Fjalar omits the formal parameter. FJALAR_DPRINTF(" location contains an unsupported operation\n"); @@ -2615,7 +2626,6 @@ static void extractOneFormalParameterVar(FunctionEntry* f, 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; - f->hasRegisterParams = True; varPtr->validLoc = 1; varPtr->locationType = REGISTER_LOCATION; FJALAR_DPRINTF(" location is register %s\n", dwarf_reg_name(regNum)); 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 51da087c..7dadbf62 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.c b/valgrind/fjalar/tests/register-params.c index 6c55259c..8507ef67 100644 --- a/valgrind/fjalar/tests/register-params.c +++ b/valgrind/fjalar/tests/register-params.c @@ -30,14 +30,14 @@ 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. +// 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. +// omitted from both the .decls and .dtrace files. __attribute__((noinline)) double identity(double d) { return d; } From 674c5349b73f29803959135df3f717513c73d640 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 21:02:37 -0700 Subject: [PATCH 07/23] Read DWARF registers through their guest-state offsets --- valgrind/fjalar/dwarf.c | 8 ++ valgrind/fjalar/fjalar_include.h | 24 +++++ valgrind/fjalar/fjalar_main.c | 168 ++++++++++++++++------------- valgrind/fjalar/fjalar_main.h | 17 +-- valgrind/fjalar/fjalar_runtime.c | 62 +---------- valgrind/fjalar/fjalar_traversal.c | 23 +++- valgrind/fjalar/readelf.c | 3 + 7 files changed, 153 insertions(+), 152 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index 080057cd..f2aa4724 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -9654,6 +9654,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_include.h b/valgrind/fjalar/fjalar_include.h index e92574ae..977700a2 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -740,6 +740,30 @@ These data structures and functions provide mechanisms for runtime traversals within data structures and arrays **********************************************************************/ +// 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 11 +#endif + +// 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 diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index c0066a3c..da20ec2e 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, or -1 if Fjalar cannot read 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,74 @@ 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), + -1, // eflags + -1, // trapno }; #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 && dwarf_reg_guest_offsets[regNum] >= 0; +} + +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; + VG_(get_shadow_regs_area)(tid, (UChar*)&value, 0/*shadowNo*/, + dwarf_reg_guest_offset(regNum), sizeof(Addr)); + return value; +} + +// If atom is DW_OP_regN or DW_OP_bregN, sets *regNum to N and returns +// True. Otherwise, returns False. A frame base of DW_OP_regN has +// offset 0, and one of DW_OP_bregN has the atom's offset. +static Bool frame_base_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; +} // located in VEX/priv/main_util.c extern void vex_bzero(void* s, UInt n); @@ -258,7 +257,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 +293,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) ); } @@ -553,6 +562,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 +629,18 @@ 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; + if (frame_base_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 +652,21 @@ 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; + } 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 (frame_base_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)); + } } } diff --git a/valgrind/fjalar/fjalar_main.h b/valgrind/fjalar/fjalar_main.h index 80df7b1b..7f597a90 100644 --- a/valgrind/fjalar/fjalar_main.h +++ b/valgrind/fjalar/fjalar_main.h @@ -185,27 +185,18 @@ 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 +// The DWARF register number of the frame pointer +#define DWARF_FP_REG 6 #else -extern Addr (*get_reg[11])( ThreadId tid ); // The DWARF register number of the stack pointer #define DWARF_SP_REG 4 +// The DWARF register number of the frame pointer +#define DWARF_FP_REG 5 #endif -// For debugging purposes, a mapping between -// DWARF location atoms and their string -// representation - -extern const HChar* dwarf_reg_string[]; - /* It is not at all clear how we should number the FP stack registers diff --git a/valgrind/fjalar/fjalar_runtime.c b/valgrind/fjalar/fjalar_runtime.c index f910808e..3d966f03 100644 --- a/valgrind/fjalar/fjalar_runtime.c +++ b/valgrind/fjalar/fjalar_runtime.c @@ -190,8 +190,6 @@ returnArrayVariableWithAddr(VarList* varList, 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) { @@ -225,65 +223,7 @@ returnArrayVariableWithAddr(VarList* varList, potentialVarBaseAddr = e->lowSP + potentialVar->byteOffset; } } - // FJALAR_DPRINTF("potential var expression size: %u\n", potentialVar->location_expression_size); - 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; - Addr 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]: %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). - reg_val = e->lowSP; - } else { - reg_val = (*get_reg[dloc->atom - DW_OP_breg0])(tid); - } - 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. - 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: %u, varType: %u\n", + FJALAR_DPRINTF("addr: %p, potential base addr: %p, staticArr: %p, ptrLevels: %u, varType: %u\n", (void*)a, (void*)potentialVarBaseAddr, potentialVar->staticArr, potentialVar->ptrLevels, (potentialVar->varType ? potentialVar->varType->decType : 0)); diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index c64786cf..b1591974 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -1030,22 +1030,35 @@ void visitVariableGroup(VariableOrigin varOrigin, } 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], + unsigned int regNum = op - DW_OP_reg0; + if (!dwarf_reg_is_readable(regNum)) { + FJALAR_DPRINTF("\tCannot read register %s; value is nonsensical\n", + dwarf_reg_name(regNum)); + var_loc = 0; + break; + } + reg_val = read_dwarf_reg(tid, regNum); + FJALAR_DPRINTF("\tObtaining register value: [%%%s]: %p\n", dwarf_reg_name(regNum), (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) { + unsigned int regNum = op - DW_OP_breg0; + if (regNum == 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_reg_is_readable(regNum)) { + reg_val = read_dwarf_reg(tid, regNum); } else { - reg_val = (*get_reg[dloc->atom - DW_OP_breg0])(tid); + FJALAR_DPRINTF("\tCannot read register %s; value is nonsensical\n", + dwarf_reg_name(regNum)); + var_loc = 0; + break; } - FJALAR_DPRINTF("\tObtaining register value: [%%%s]: %p\n", dwarf_reg_string[dloc->atom - DW_OP_breg0], + FJALAR_DPRINTF("\tObtaining 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); diff --git a/valgrind/fjalar/readelf.c b/valgrind/fjalar/readelf.c index 723d202b..3a8ea721 100644 --- a/valgrind/fjalar/readelf.c +++ b/valgrind/fjalar/readelf.c @@ -22611,6 +22611,9 @@ fjalar_process_file (const char * file_name) return FALSE; } + // Register names in debugging output, such as dwarf_reg_name(). + init_dwarf_regnames_by_elf_machine_code (filedata->file_header.e_machine); + // FJALAR_DPRINTF("before process_section_headers()\n"); if (! process_section_headers (filedata)) From d3c21c6937a2ec567292388386854e0f6801182e Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 21:02:39 -0700 Subject: [PATCH 08/23] Omit a formal parameter whose location Fjalar cannot read from the .decls file too --- Makefile | 1 + valgrind/fjalar/dwarf.c | 47 ++++++++++++--- valgrind/fjalar/fjalar_traversal.c | 22 +++---- valgrind/fjalar/generate_fjalar_entries.c | 5 +- valgrind/fjalar/tests/omitted-params-test.sh | 63 ++++++++++++++++++++ valgrind/fjalar/tests/omitted-params.c | 39 ++++++++++++ valgrind/fjalar/tests/omitted-params.goal | 2 + valgrind/fjalar/typedata.c | 22 +++++++ valgrind/fjalar/typedata.h | 9 +++ 9 files changed, 191 insertions(+), 19 deletions(-) create mode 100755 valgrind/fjalar/tests/omitted-params-test.sh create mode 100644 valgrind/fjalar/tests/omitted-params.c create mode 100644 valgrind/fjalar/tests/omitted-params.goal diff --git a/Makefile b/Makefile index 284f4bad..b65013a4 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ test: # Fjalar tests that do not depend on Daikon fjalar-test: valgrind/fjalar/tests/library-compilation-unit-test.sh + valgrind/fjalar/tests/omitted-params-test.sh # Kvasir tests daikon-test: ../daikon diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index 080057cd..d2f52815 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1447,6 +1447,11 @@ decode_location_expression (unsigned char * data, while (data < end) { + /* The size of a formal parameter's dwarf_stack before this op, or + -1 if not harvesting a formal parameter's location. */ + int param_stack_size_before + = (ok_to_harvest ? formal_param_location_stack_size (entry) : -1); + op = *data++; if(ll) {ll->atom = op;} @@ -1469,10 +1474,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 +1527,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); @@ -2074,10 +2095,20 @@ decode_location_expression (unsigned char * data, printf (_("(User defined location op 0x%x)"), op); else printf (_("(Unknown location op 0x%x)"), op); + if (param_stack_size_before >= 0) + harvest_formal_param_location_unsupported (entry); /* No way to tell where the next op is, so just bail. */ return need_frame_base; } + /* An op that is not in a formal parameter's dwarf_stack, such as + DW_OP_piece, DW_OP_stack_value, or DW_OP_plus (which Fjalar's + evaluator, having no operand stack, cannot apply), makes the + location one that Fjalar does not support. */ + if (param_stack_size_before >= 0 && op != DW_OP_nop + && formal_param_location_stack_size (entry) == param_stack_size_before) + harvest_formal_param_location_unsupported (entry); + /* Separate the ops. */ if (data < end) printf ("; "); diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index c64786cf..773cd98b 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -929,18 +929,20 @@ void visitVariableGroup(VariableOrigin varOrigin, 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) { 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; - } - 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))); @@ -1024,7 +1026,7 @@ 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; diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index fc89f77d..b86d1759 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2573,7 +2573,10 @@ static void extractOneFormalParameterVar(FunctionEntry* f, varPtr->location_expression_size = paramPtr->dwarf_stack_size; } - if (paramPtr->location_type == LT_FP_OFFSET) { + if (paramPtr->location_has_unsupported_op) { + // varPtr->validLoc remains 0, so Fjalar omits the formal parameter. + FJALAR_DPRINTF(" location contains an unsupported operation\n"); + } else if (paramPtr->location_type == LT_FP_OFFSET) { varPtr->validLoc = paramPtr->valid_loc; varPtr->locationType = FP_OFFSET_LOCATION; varPtr->byteOffset = paramPtr->location; diff --git a/valgrind/fjalar/tests/omitted-params-test.sh b/valgrind/fjalar/tests/omitted-params-test.sh new file mode 100755 index 00000000..f8c9e528 --- /dev/null +++ b/valgrind/fjalar/tests/omitted-params-test.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# 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 \ + -o "${output_dir}/omitted-params" "${test_dir}/omitted-params.c" + +decls="${output_dir}/omitted-params.decls" +dtrace="${output_dir}/omitted-params.dtrace" +status=0 +"${valgrind}" --tool=fjalar \ + --decls-file="${decls}" --dtrace-file="${dtrace}" \ + "${output_dir}/omitted-params" \ + > "${output_dir}/omitted-params.out" 2>&1 || status=$? + +if [ "${status}" -ne 0 ]; then + echo "$0: FAILED: Fjalar exited with status ${status}" >&2 + cat "${output_dir}/omitted-params.out" >&2 + exit 1 +fi + +# One line per non-global variable declared at each program point other than +# main's: "decls ", followed by one line per non-global +# variable at the first occurrence of each program point other than main's: +# "dtrace ". +actual="${output_dir}/omitted-params.actual" +awk '/^ppt / { ppt = $2 } + /^ variable / { if ($2 !~ /^::/ && ppt !~ /^\.\.main\(/) + print "decls", ppt, $2 }' \ + "${decls}" > "${actual}" +awk '/^\.\.[a-z]*\(\):::/ { ppt = $0; getline; getline + if (ppt ~ /^\.\.main\(/ || seen[ppt]++) ppt = "" + next } + ppt == "" { next } + /^$/ { ppt = ""; next } + { var = $0; getline; getline + if (var !~ /^::/) print "dtrace", ppt, var }' \ + "${dtrace}" >> "${actual}" + +if ! diff -u "${test_dir}/omitted-params.goal" "${actual}" >&2; then + echo "$0: FAILED: output differs from omitted-params.goal" >&2 + exit 1 +fi + +echo "$0: PASSED" diff --git a/valgrind/fjalar/tests/omitted-params.c b/valgrind/fjalar/tests/omitted-params.c new file mode 100644 index 00000000..ecd6a224 --- /dev/null +++ b/valgrind/fjalar/tests/omitted-params.c @@ -0,0 +1,39 @@ +/* + 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 locations Fjalar cannot read. Fjalar omits them from both the .decls +// and .dtrace files. See omitted-params-test.sh. + +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. +__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. +__attribute__((noinline)) double identity(double d) { + return d; +} + +int main(void) { + struct pair p = {5, 2}; + split(p); + return (int)identity(1.5) == 12345; +} diff --git a/valgrind/fjalar/tests/omitted-params.goal b/valgrind/fjalar/tests/omitted-params.goal new file mode 100644 index 00000000..e626e6b3 --- /dev/null +++ b/valgrind/fjalar/tests/omitted-params.goal @@ -0,0 +1,2 @@ +decls ..identity():::EXIT0 return +dtrace ..identity():::EXIT0 return diff --git a/valgrind/fjalar/typedata.c b/valgrind/fjalar/typedata.c index b96e81ee..d5fade8f 100644 --- a/valgrind/fjalar/typedata.c +++ b/valgrind/fjalar/typedata.c @@ -1234,6 +1234,27 @@ char harvest_formal_param_location_offset(dwarf_entry* e, long value) return 0; } +int formal_param_location_stack_size(dwarf_entry* e) +{ + if ((e == 0) || (e->entry_ptr == 0) || !tag_is_formal_parameter(e->tag_name)) + return -1; + return ((formal_parameter*)e->entry_ptr)->dwarf_stack_size; +} + +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; +} + char harvest_data_member_location(dwarf_entry* e, unsigned long value) { unsigned long tag; @@ -1914,6 +1935,7 @@ 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->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..3314ed00 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -347,6 +347,11 @@ typedef struct // way to get the parameter location unsigned int valid_loc; + // True if the location expression contains an operation that + // dwarf_stack does not record, such as DW_OP_piece or + // DW_OP_stack_value. Fjalar cannot read such a location. + char location_has_unsupported_op; + unsigned long abstract_origin_ID; // See comment in the function struct definition // for the uses of this. @@ -568,6 +573,10 @@ 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); +// Returns the size of the dwarf_stack of e, or -1 if e is not a +// formal parameter. +int formal_param_location_stack_size(dwarf_entry* e); +char harvest_formal_param_location_unsupported(dwarf_entry* e); 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); From 29692dd516bb82d5ff6fc57e0ecfff56dbfce5d3 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 16:29:49 -0700 Subject: [PATCH 09/23] Share one supported-op predicate; fix overflow, fix-up, and register-value bugs * location_op_is_supported() is the single definition of which DWARF ops Fjalar's evaluator can apply; decode_location_expression uses it to flag unsupported formal-parameter locations. It also rejects DW_OP_reg*/DW_OP_breg* for registers that get_reg cannot read. * A location expression longer than MAX_DWARF_OPS marks the parameter unsupported instead of failing an assertion. * A parameter with an unsupported location gets no location_expression, and the `this` and main argc/argv fix-ups no longer force it valid. * The value of a DW_OP_reg* location is stored in VariableEntry.regValue instead of in a local variable whose lifetime ends before it is read. * Remove an unused copy of the evaluator from fjalar_runtime.c. --- valgrind/fjalar/dwarf.c | 21 +++----- valgrind/fjalar/fjalar_dwarf.h | 5 ++ valgrind/fjalar/fjalar_include.h | 10 ++++ valgrind/fjalar/fjalar_runtime.c | 60 ----------------------- valgrind/fjalar/fjalar_traversal.c | 38 +++++++++----- valgrind/fjalar/generate_fjalar_entries.c | 43 +++++++++------- valgrind/fjalar/typedata.c | 12 ++--- valgrind/fjalar/typedata.h | 10 ++-- 8 files changed, 81 insertions(+), 118 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index d2f52815..db54b8e9 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1447,15 +1447,16 @@ decode_location_expression (unsigned char * data, while (data < end) { - /* The size of a formal parameter's dwarf_stack before this op, or - -1 if not harvesting a formal parameter's location. */ - int param_stack_size_before - = (ok_to_harvest ? formal_param_location_stack_size (entry) : -1); - op = *data++; if(ll) {ll->atom = op;} + /* An op that Fjalar's evaluator cannot apply, such as DW_OP_piece, + DW_OP_stack_value, or DW_OP_plus (the evaluator has no operand + stack), makes a formal parameter's location unreadable. */ + if (ok_to_harvest && op != DW_OP_nop && !location_op_is_supported (op)) + harvest_formal_param_location_unsupported (entry); + switch (op) { case DW_OP_addr: @@ -2095,20 +2096,10 @@ decode_location_expression (unsigned char * data, printf (_("(User defined location op 0x%x)"), op); else printf (_("(Unknown location op 0x%x)"), op); - if (param_stack_size_before >= 0) - harvest_formal_param_location_unsupported (entry); /* No way to tell where the next op is, so just bail. */ return need_frame_base; } - /* An op that is not in a formal parameter's dwarf_stack, such as - DW_OP_piece, DW_OP_stack_value, or DW_OP_plus (which Fjalar's - evaluator, having no operand stack, cannot apply), makes the - location one that Fjalar does not support. */ - if (param_stack_size_before >= 0 && op != DW_OP_nop - && formal_param_location_stack_size (entry) == param_stack_size_before) - harvest_formal_param_location_unsupported (entry); - /* Separate the ops. */ if (data < end) printf ("; "); diff --git a/valgrind/fjalar/fjalar_dwarf.h b/valgrind/fjalar/fjalar_dwarf.h index f3e97557..5ebb7680 100644 --- a/valgrind/fjalar/fjalar_dwarf.h +++ b/valgrind/fjalar/fjalar_dwarf.h @@ -41,4 +41,9 @@ 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); +// 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..d02904fa 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 @@ -411,6 +416,11 @@ typedef struct _VariableEntry { Addr entryLoc; Addr entryLocGuest; + // If the location is a register (DW_OP_reg*), which has no address, + // visitVariableGroup copies the register's value here and reads the + // variable from this field. + Addr regValue; // mutable + // g++ will not emit a memory location for (all?) constant primitives // even on O0, so we will have to make a special case for constants // without valid memory locations. It is always preferable to use the diff --git a/valgrind/fjalar/fjalar_runtime.c b/valgrind/fjalar/fjalar_runtime.c index f910808e..a60347a4 100644 --- a/valgrind/fjalar/fjalar_runtime.c +++ b/valgrind/fjalar/fjalar_runtime.c @@ -190,8 +190,6 @@ returnArrayVariableWithAddr(VarList* varList, 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) { @@ -225,64 +223,6 @@ returnArrayVariableWithAddr(VarList* varList, potentialVarBaseAddr = e->lowSP + potentialVar->byteOffset; } } - // FJALAR_DPRINTF("potential var expression size: %u\n", potentialVar->location_expression_size); - 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; - Addr 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]: %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). - reg_val = e->lowSP; - } else { - reg_val = (*get_reg[dloc->atom - DW_OP_breg0])(tid); - } - 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. - 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: %u, varType: %u\n", (void*)a, (void*)potentialVarBaseAddr, potentialVar->staticArr, potentialVar->ptrLevels, (potentialVar->varType ? potentialVar->varType->decType : 0)); diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index 773cd98b..261cf8c6 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -852,6 +852,25 @@ void visitClassMemberVariables(VisitArgs* args) { FJALAR_DPRINTF("Exit visitClassMemberVariables\n"); } +// Returns true if get_reg can read DWARF register regno. +static int dwarf_reg_is_readable(unsigned int regno) { + return ((regno < sizeof(get_reg) / sizeof(get_reg[0])) + && (get_reg[regno] != NULL)); +} + +int location_op_is_supported(unsigned int op) { + if ((op >= DW_OP_reg0) && (op <= DW_OP_reg31)) { + return dwarf_reg_is_readable(op - DW_OP_reg0); + } + if ((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { + return dwarf_reg_is_readable(op - DW_OP_breg0); + } + return ((op == DW_OP_deref) + || ((op >= DW_OP_const1u) && (op <= DW_OP_consts)) + || (op == DW_OP_plus_uconst) + || (op == DW_OP_fbreg)); +} + // Visits an entire group of variables, depending on the value of varOrigin: // If varOrigin == GLOBAL_VAR, then visit all global variables // If varOrigin == FUNCTION_FORMAL_PARAM, then visit all formal parameters @@ -1008,16 +1027,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; - - } 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; @@ -1031,11 +1047,12 @@ void visitVariableGroup(VariableOrigin varOrigin, 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); + // Get value located in architectural register. The value + // must outlive this loop, so store it in var. + var->regValue = (*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; + (void *)var->regValue); + var_loc = (Addr)&var->regValue; } else if((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { // Get value pointed to by architectural register @@ -1059,8 +1076,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); } diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index b86d1759..48ecd594 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2562,27 +2562,30 @@ 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); - } - varPtr->location_expression_size = paramPtr->dwarf_stack_size; - } - if (paramPtr->location_has_unsupported_op) { // varPtr->validLoc remains 0, so Fjalar omits the formal parameter. FJALAR_DPRINTF(" location contains an unsupported operation\n"); - } else 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; + 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; + } - 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; + + FJALAR_DPRINTF(" location_type: %u, byteOffset: %x\n", varPtr->locationType, (unsigned int)varPtr->byteOffset); + } } FJALAR_DPRINTF("EXIT extractOneFormalParameterVar\n"); @@ -3151,6 +3154,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); @@ -3162,6 +3166,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); @@ -3213,7 +3218,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/typedata.c b/valgrind/fjalar/typedata.c index d5fade8f..f543ffcd 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,13 +1237,6 @@ char harvest_formal_param_location_offset(dwarf_entry* e, long value) return 0; } -int formal_param_location_stack_size(dwarf_entry* e) -{ - if ((e == 0) || (e->entry_ptr == 0) || !tag_is_formal_parameter(e->tag_name)) - return -1; - return ((formal_parameter*)e->entry_ptr)->dwarf_stack_size; -} - char harvest_formal_param_location_unsupported(dwarf_entry* e) { if ((e == 0) || (e->entry_ptr == 0)) diff --git a/valgrind/fjalar/typedata.h b/valgrind/fjalar/typedata.h index 3314ed00..9ea32c57 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -347,9 +347,10 @@ typedef struct // way to get the parameter location unsigned int valid_loc; - // True if the location expression contains an operation that - // dwarf_stack does not record, such as DW_OP_piece or - // DW_OP_stack_value. Fjalar cannot read such a location. + // True if the location expression contains an operation that does + // not satisfy location_op_is_supported, such as DW_OP_piece or + // DW_OP_stack_value, or contains more than MAX_DWARF_OPS operations. + // Fjalar cannot read such a location. char location_has_unsupported_op; unsigned long abstract_origin_ID; // See comment in the function struct definition @@ -573,9 +574,6 @@ 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); -// Returns the size of the dwarf_stack of e, or -1 if e is not a -// formal parameter. -int formal_param_location_stack_size(dwarf_entry* e); char harvest_formal_param_location_unsupported(dwarf_entry* e); 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); From 0f0825395ea6eba2685dc45de81ed74bc7c7ed19 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 16:45:08 -0700 Subject: [PATCH 10/23] Address code review findings on DWARF register access - Declare R8-R15 as read by the function-exit helper, too. - For DW_OP_bregN on the frame pointer, use its value at function entry. - Keep a register-located variable's value alive until visitVariable runs. - Share DW_OP_regN/DW_OP_bregN decoding via dwarf_op_reg_num. - Remove a redundant init_dwarf_regnames_by_elf_machine_code call. - Simplify the x86 register table; keep its size private. - Log when the CFA register cannot be read. --- valgrind/fjalar/fjalar_include.h | 28 +++++++++++++------ valgrind/fjalar/fjalar_main.c | 43 ++++++++++++++++++----------- valgrind/fjalar/fjalar_main.h | 11 -------- valgrind/fjalar/fjalar_traversal.c | 44 +++++++++++++++--------------- valgrind/fjalar/readelf.c | 3 -- 5 files changed, 69 insertions(+), 60 deletions(-) diff --git a/valgrind/fjalar/fjalar_include.h b/valgrind/fjalar/fjalar_include.h index 977700a2..f70caebc 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -688,6 +688,10 @@ typedef struct _FunctionEntry { // used in calculating the address of variables from DWARF location expressions Addr FP; + // The value of the frame pointer register (%ebp, %rbp) for this + // invocation of the function, after the prologue has executed. + Addr entryFPReg; + UInt nonce; } FunctionEntry; @@ -740,22 +744,26 @@ These data structures and functions provide mechanisms for runtime traversals within data structures and arrays **********************************************************************/ -// One more than the largest DWARF register number that Fjalar can -// read. #if defined(VGA_amd64) -#define FJALAR_NUM_DWARF_REGS 16 +// The DWARF register number of the stack pointer +#define DWARF_SP_REG 7 +// The DWARF register number of the frame pointer +#define DWARF_FP_REG 6 #else -#define FJALAR_NUM_DWARF_REGS 11 +// The DWARF register number of the stack pointer +#define DWARF_SP_REG 4 +// The DWARF register number of the frame pointer +#define DWARF_FP_REG 5 #endif +// 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); @@ -817,6 +825,10 @@ typedef struct { Addr lowSP; + + // The value of the frame pointer register (%ebp, %rbp) after the + // prologue has executed. + Addr entryFPReg; } FunctionExecutionState; diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index da20ec2e..d0b42fff 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -120,6 +120,9 @@ const HChar* executable_filename = 0; */ #if defined(VGA_amd64) +// One more than the largest DWARF register number that Fjalar can +// read. +#define FJALAR_NUM_DWARF_REGS 16 // AMD64 Dwarf to Architecture mapping is (thankfully) specified // in the AMD64 ABI (http://x86-64.org/documentation/abi.pdf) static const Int dwarf_reg_guest_offsets[FJALAR_NUM_DWARF_REGS] = { @@ -141,6 +144,7 @@ static const Int dwarf_reg_guest_offsets[FJALAR_NUM_DWARF_REGS] = { offsetof(VexGuestArchState, guest_R15), }; #else +#define FJALAR_NUM_DWARF_REGS 9 static const Int dwarf_reg_guest_offsets[FJALAR_NUM_DWARF_REGS] = { offsetof(VexGuestArchState, guest_EAX), offsetof(VexGuestArchState, guest_ECX), @@ -151,34 +155,24 @@ static const Int dwarf_reg_guest_offsets[FJALAR_NUM_DWARF_REGS] = { offsetof(VexGuestArchState, guest_ESI), offsetof(VexGuestArchState, guest_EDI), offsetof(VexGuestArchState, guest_EIP), - -1, // eflags - -1, // trapno }; #endif Bool dwarf_reg_is_readable(UInt regNum) { - return regNum < FJALAR_NUM_DWARF_REGS && dwarf_reg_guest_offsets[regNum] >= 0; -} - -Int dwarf_reg_guest_offset(UInt regNum) -{ - tl_assert(dwarf_reg_is_readable(regNum)); - return dwarf_reg_guest_offsets[regNum]; + return regNum < FJALAR_NUM_DWARF_REGS; } 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_offset(regNum), sizeof(Addr)); + dwarf_reg_guest_offsets[regNum], sizeof(Addr)); return value; } -// If atom is DW_OP_regN or DW_OP_bregN, sets *regNum to N and returns -// True. Otherwise, returns False. A frame base of DW_OP_regN has -// offset 0, and one of DW_OP_bregN has the atom's offset. -static Bool frame_base_reg_num(UInt atom, UInt* regNum) +Bool dwarf_op_reg_num(UInt atom, UInt* regNum) { if (atom >= DW_OP_breg0 && atom <= DW_OP_breg31) { *regNum = atom - DW_OP_breg0; @@ -492,7 +486,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; @@ -531,6 +529,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) ); } @@ -636,7 +640,8 @@ void enter_function(FunctionEntry* f) ll->atom = DW_OP_breg0 + DWARF_FP_REG; ll->atom_offset = 2 * sizeof(Addr); } - if (frame_base_reg_num(ll->atom, ®Num) && dwarf_reg_is_readable(regNum)) { + // 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", @@ -654,6 +659,9 @@ void enter_function(FunctionEntry* f) FJALAR_DPRINTF("\tFound debug frame with reg %x and offset %lx\n", df->cfa_reg, 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; } @@ -661,7 +669,7 @@ void enter_function(FunctionEntry* f) } } else { FJALAR_DPRINTF("\tsimple location expression\n"); - if (frame_base_reg_num(f->frame_base_atom, ®Num) && dwarf_reg_is_readable(regNum)) { + 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", @@ -693,6 +701,8 @@ void enter_function(FunctionEntry* f) newEntry->func->FP = frame_ptr; newEntry->func->lowestSP = stack_ptr; newEntry->FP = frame_ptr; + newEntry->func->entryFPReg = VG_(get_FP)(tid); + newEntry->entryFPReg = newEntry->func->entryFPReg; newEntry->lowSP = stack_ptr; newEntry->lowestSP = stack_ptr; newEntry->xAX = 0; @@ -795,6 +805,7 @@ void exit_function(FunctionEntry* f) top->func->guestStackEnd = top->func->guestStackStart + top->virtualStackByteSize; top->func->lowestVirtSP = (Addr)top->virtualStack; top->func->FP = top->FP; + top->func->entryFPReg = top->entryFPReg; top->func->lowestSP = top->func->guestStackStart; FJALAR_DPRINTF("\tState of Guest Stack [%p - %p] \n", (void *)top->lowestSP, (void *)(top->lowestSP + top->virtualStackByteSize)); diff --git a/valgrind/fjalar/fjalar_main.h b/valgrind/fjalar/fjalar_main.h index 7f597a90..897a3c57 100644 --- a/valgrind/fjalar/fjalar_main.h +++ b/valgrind/fjalar/fjalar_main.h @@ -185,17 +185,6 @@ This is called from hooks within mac_shared.h char* fjalar_program_stdout_filename; char* fjalar_program_stderr_filename; -#if defined(VGA_amd64) -// The DWARF register number of the stack pointer -#define DWARF_SP_REG 7 -// The DWARF register number of the frame pointer -#define DWARF_FP_REG 6 -#else -// The DWARF register number of the stack pointer -#define DWARF_SP_REG 4 -// The DWARF register number of the frame pointer -#define DWARF_FP_REG 5 -#endif /* diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index b1591974..8e50cdd2 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -916,6 +916,10 @@ void visitVariableGroup(VariableOrigin varOrigin, VariableEntry* var = nextVar(varIt); Addr basePtrValue = (Addr) NULL; Addr basePtrValueGuest = (Addr) NULL; + // For a variable that is located in a register, the register's + // value. basePtrValue points here, so this must outlive the call + // to visitVariable. + Addr reg_val; if (!var->name) { printf( " Warning! Weird null variable name!\n"); @@ -1009,7 +1013,7 @@ void visitVariableGroup(VariableOrigin varOrigin, for(i = 0; i < var->location_expression_size; i++ ) { dwarf_location *dloc = &(var->location_expression[i]); unsigned int op = dloc->atom; - Addr reg_val; + UInt regNum; if(op == DW_OP_addr) { // DWARF supplied address @@ -1028,28 +1032,20 @@ void visitVariableGroup(VariableOrigin varOrigin, // 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 - unsigned int regNum = op - DW_OP_reg0; - if (!dwarf_reg_is_readable(regNum)) { - FJALAR_DPRINTF("\tCannot read register %s; value is nonsensical\n", - dwarf_reg_name(regNum)); - var_loc = 0; - break; - } - reg_val = read_dwarf_reg(tid, regNum); - FJALAR_DPRINTF("\tObtaining register value: [%%%s]: %p\n", dwarf_reg_name(regNum), - (void *)reg_val); - var_loc = (Addr)®_val; - - } else if((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { - // Get value pointed to by architectural register - unsigned int regNum = op - DW_OP_breg0; - if (regNum == DWARF_SP_REG) { + } 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. + Bool isBreg = (op >= DW_OP_breg0) && (op <= DW_OP_breg31); + if (isBreg && regNum == 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 (isBreg && regNum == DWARF_FP_REG) { + // Use the frame pointer register at function entry (after + // the prologue). At function exit, the epilogue has + // already restored the caller's frame pointer. + reg_val = funcPtr->entryFPReg; } else if (dwarf_reg_is_readable(regNum)) { reg_val = read_dwarf_reg(tid, regNum); } else { @@ -1060,9 +1056,13 @@ void visitVariableGroup(VariableOrigin varOrigin, } FJALAR_DPRINTF("\tObtaining 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); + if (isBreg) { + 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 { + var_loc = (Addr)®_val; + } } else if(op == DW_OP_fbreg) { // Get value located at an offset from the FRAME_BASE. diff --git a/valgrind/fjalar/readelf.c b/valgrind/fjalar/readelf.c index 3a8ea721..723d202b 100644 --- a/valgrind/fjalar/readelf.c +++ b/valgrind/fjalar/readelf.c @@ -22611,9 +22611,6 @@ fjalar_process_file (const char * file_name) return FALSE; } - // Register names in debugging output, such as dwarf_reg_name(). - init_dwarf_regnames_by_elf_machine_code (filedata->file_header.e_machine); - // FJALAR_DPRINTF("before process_section_headers()\n"); if (! process_section_headers (filedata)) From 071458bf89a0e7bf5f936affd8680205ca03ebbc Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 16:55:51 -0700 Subject: [PATCH 11/23] Do not save the frame pointer at entry; #90 saves all registers Also correct the comment on dwarf_reg_guest_offsets, which has no -1 entries. --- valgrind/fjalar/fjalar_include.h | 8 -------- valgrind/fjalar/fjalar_main.c | 5 +---- valgrind/fjalar/fjalar_traversal.c | 5 ----- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/valgrind/fjalar/fjalar_include.h b/valgrind/fjalar/fjalar_include.h index f70caebc..e6777b76 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -688,10 +688,6 @@ typedef struct _FunctionEntry { // used in calculating the address of variables from DWARF location expressions Addr FP; - // The value of the frame pointer register (%ebp, %rbp) for this - // invocation of the function, after the prologue has executed. - Addr entryFPReg; - UInt nonce; } FunctionEntry; @@ -825,10 +821,6 @@ typedef struct { Addr lowSP; - - // The value of the frame pointer register (%ebp, %rbp) after the - // prologue has executed. - Addr entryFPReg; } FunctionExecutionState; diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index d0b42fff..a379bc7b 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -96,7 +96,7 @@ Bool doing_debug_print = False; const HChar* executable_filename = 0; // Mapping between DWARF register numbers and the guest-state offset -// of the register, or -1 if Fjalar cannot read the register. +// of the register. // Below comment is ripped from GCC 4.3.1 /* Define the register numbers to be used in Dwarf debugging information. @@ -701,8 +701,6 @@ void enter_function(FunctionEntry* f) newEntry->func->FP = frame_ptr; newEntry->func->lowestSP = stack_ptr; newEntry->FP = frame_ptr; - newEntry->func->entryFPReg = VG_(get_FP)(tid); - newEntry->entryFPReg = newEntry->func->entryFPReg; newEntry->lowSP = stack_ptr; newEntry->lowestSP = stack_ptr; newEntry->xAX = 0; @@ -805,7 +803,6 @@ void exit_function(FunctionEntry* f) top->func->guestStackEnd = top->func->guestStackStart + top->virtualStackByteSize; top->func->lowestVirtSP = (Addr)top->virtualStack; top->func->FP = top->FP; - top->func->entryFPReg = top->entryFPReg; top->func->lowestSP = top->func->guestStackStart; FJALAR_DPRINTF("\tState of Guest Stack [%p - %p] \n", (void *)top->lowestSP, (void *)(top->lowestSP + top->virtualStackByteSize)); diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index 8e50cdd2..6d08a5d5 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -1041,11 +1041,6 @@ void visitVariableGroup(VariableOrigin varOrigin, // prologue). At function exit, the epilogue has already // popped the stack frame. reg_val = funcPtr->guestStackStart + VG_STACK_REDZONE_SZB; - } else if (isBreg && regNum == DWARF_FP_REG) { - // Use the frame pointer register at function entry (after - // the prologue). At function exit, the epilogue has - // already restored the caller's frame pointer. - reg_val = funcPtr->entryFPReg; } else if (dwarf_reg_is_readable(regNum)) { reg_val = read_dwarf_reg(tid, regNum); } else { From 1656f48965ff49730dade07a8b22dd6c52e6bba0 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 17:01:40 -0700 Subject: [PATCH 12/23] Treat a register location as unsupported; test a location with an offset and an unsupported op * visitVariableGroup reads a variable from guest memory, and a register has no address, A and V bits, or DynComp tags, so DW_OP_reg* is not a supported location op. Remove VariableEntry.regValue, which pointed the variable at tool memory. * omitted-params-test.sh rewrites a parameter's location to "DW_OP_fbreg -8; DW_OP_stack_value", which Fjalar would otherwise treat as a valid frame offset. --- valgrind/fjalar/dwarf.c | 7 +++--- valgrind/fjalar/fjalar_include.h | 5 ---- valgrind/fjalar/fjalar_traversal.c | 14 +++-------- valgrind/fjalar/tests/omitted-params-test.sh | 25 ++++++++++++++++++-- valgrind/fjalar/tests/omitted-params.c | 13 +++++++++- valgrind/fjalar/typedata.h | 5 ++-- 6 files changed, 45 insertions(+), 24 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index db54b8e9..45fff718 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1451,9 +1451,10 @@ decode_location_expression (unsigned char * data, if(ll) {ll->atom = op;} - /* An op that Fjalar's evaluator cannot apply, such as DW_OP_piece, - DW_OP_stack_value, or DW_OP_plus (the evaluator has no operand - stack), makes a formal parameter's location unreadable. */ + /* An op that Fjalar's evaluator cannot apply, such as DW_OP_reg*, + DW_OP_piece, DW_OP_stack_value, or DW_OP_plus (the evaluator has + no operand stack), makes a formal parameter's location + unreadable. */ if (ok_to_harvest && op != DW_OP_nop && !location_op_is_supported (op)) harvest_formal_param_location_unsupported (entry); diff --git a/valgrind/fjalar/fjalar_include.h b/valgrind/fjalar/fjalar_include.h index d02904fa..98089c9f 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -416,11 +416,6 @@ typedef struct _VariableEntry { Addr entryLoc; Addr entryLocGuest; - // If the location is a register (DW_OP_reg*), which has no address, - // visitVariableGroup copies the register's value here and reads the - // variable from this field. - Addr regValue; // mutable - // g++ will not emit a memory location for (all?) constant primitives // even on O0, so we will have to make a special case for constants // without valid memory locations. It is always preferable to use the diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index 261cf8c6..2ab15396 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -858,10 +858,10 @@ static int dwarf_reg_is_readable(unsigned int regno) { && (get_reg[regno] != NULL)); } +// A register location (DW_OP_reg*) is not supported: visitVariableGroup +// reads a variable from guest memory, and a register has no address, no +// A and V bits, and no DynComp tags. int location_op_is_supported(unsigned int op) { - if ((op >= DW_OP_reg0) && (op <= DW_OP_reg31)) { - return dwarf_reg_is_readable(op - DW_OP_reg0); - } if ((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { return dwarf_reg_is_readable(op - DW_OP_breg0); } @@ -1046,14 +1046,6 @@ void visitVariableGroup(VariableOrigin varOrigin, // 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. The value - // must outlive this loop, so store it in var. - var->regValue = (*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 *)var->regValue); - var_loc = (Addr)&var->regValue; - } 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) { diff --git a/valgrind/fjalar/tests/omitted-params-test.sh b/valgrind/fjalar/tests/omitted-params-test.sh index f8c9e528..549986a7 100755 --- a/valgrind/fjalar/tests/omitted-params-test.sh +++ b/valgrind/fjalar/tests/omitted-params-test.sh @@ -20,8 +20,29 @@ trap 'rm -rf "${output_dir}"' EXIT # 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 \ - -o "${output_dir}/omitted-params" "${test_dir}/omitted-params.c" +gcc -gdwarf-4 -gno-variable-location-views -no-pie -O1 -S -dA \ + -o "${output_dir}/omitted-params.s" "${test_dir}/omitted-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 x 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/ && /"x\\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 x: " $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 x" > "/dev/stderr"; exit 1 } }' \ + "${output_dir}/omitted-params.s" > "${output_dir}/omitted-params-patched.s" +gcc -gdwarf-4 -no-pie \ + -o "${output_dir}/omitted-params" "${output_dir}/omitted-params-patched.s" decls="${output_dir}/omitted-params.decls" dtrace="${output_dir}/omitted-params.dtrace" diff --git a/valgrind/fjalar/tests/omitted-params.c b/valgrind/fjalar/tests/omitted-params.c index ecd6a224..a053cb9f 100644 --- a/valgrind/fjalar/tests/omitted-params.c +++ b/valgrind/fjalar/tests/omitted-params.c @@ -26,14 +26,25 @@ __attribute__((noinline)) void split(struct pair p) { sink = p.second; } -// d is in a floating-point register, which Fjalar cannot read, so d is +// d is in a register (DW_OP_reg*), which Fjalar does not support, so d is // omitted. __attribute__((noinline)) double identity(double d) { return d; } +// Unoptimized, x is in the stack frame (DW_OP_fbreg). omitted-params-test.sh +// rewrites x's location to "DW_OP_fbreg -8; DW_OP_stack_value", which Fjalar +// does not support, so x is omitted. +__attribute__((noinline, optimize("O0"))) void stack_value(long x) { + // buf makes x's frame offset large enough to need 2 bytes. + volatile char buf[200]; + buf[0] = 1; + sink = x + buf[0]; +} + int main(void) { struct pair p = {5, 2}; split(p); + stack_value(7); return (int)identity(1.5) == 12345; } diff --git a/valgrind/fjalar/typedata.h b/valgrind/fjalar/typedata.h index 9ea32c57..8208a83d 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -348,8 +348,9 @@ typedef struct unsigned int valid_loc; // True if the location expression contains an operation that does - // not satisfy location_op_is_supported, such as DW_OP_piece or - // DW_OP_stack_value, or contains more than MAX_DWARF_OPS operations. + // not satisfy location_op_is_supported, such as DW_OP_reg*, + // DW_OP_piece, or DW_OP_stack_value, or contains more than + // MAX_DWARF_OPS operations. // Fjalar cannot read such a location. char location_has_unsupported_op; From b22db92e1af567ef4decaa57992b8334fef03c71 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 17:02:55 -0700 Subject: [PATCH 13/23] Reduce this pull request to omitting unreadable parameters from the .decls file Unsupported-op detection and the removal of the unused evaluator in fjalar_runtime.c move to separate pull requests. --- valgrind/fjalar/dwarf.c | 39 +++---------- valgrind/fjalar/fjalar_dwarf.h | 5 -- valgrind/fjalar/fjalar_include.h | 5 -- valgrind/fjalar/fjalar_runtime.c | 60 ++++++++++++++++++++ valgrind/fjalar/fjalar_traversal.c | 38 +++++-------- valgrind/fjalar/generate_fjalar_entries.c | 42 ++++++-------- valgrind/fjalar/tests/omitted-params-test.sh | 25 +------- valgrind/fjalar/tests/omitted-params.c | 14 +---- valgrind/fjalar/typedata.c | 20 +------ valgrind/fjalar/typedata.h | 8 --- 10 files changed, 103 insertions(+), 153 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index 45fff718..080057cd 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1451,13 +1451,6 @@ decode_location_expression (unsigned char * data, if(ll) {ll->atom = op;} - /* An op that Fjalar's evaluator cannot apply, such as DW_OP_reg*, - DW_OP_piece, DW_OP_stack_value, or DW_OP_plus (the evaluator has - no operand stack), makes a formal parameter's location - unreadable. */ - if (ok_to_harvest && op != DW_OP_nop && !location_op_is_supported (op)) - harvest_formal_param_location_unsupported (entry); - switch (op) { case DW_OP_addr: @@ -1476,20 +1469,10 @@ 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: @@ -1529,22 +1512,16 @@ 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, 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)); + 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); break; case DW_OP_const8s: - 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)); + 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); break; case DW_OP_constu: READ_ULEB (uvalue, data, end); diff --git a/valgrind/fjalar/fjalar_dwarf.h b/valgrind/fjalar/fjalar_dwarf.h index 5ebb7680..f3e97557 100644 --- a/valgrind/fjalar/fjalar_dwarf.h +++ b/valgrind/fjalar/fjalar_dwarf.h @@ -41,9 +41,4 @@ 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); -// 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 98089c9f..e92574ae 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -402,11 +402,6 @@ 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 diff --git a/valgrind/fjalar/fjalar_runtime.c b/valgrind/fjalar/fjalar_runtime.c index a60347a4..f910808e 100644 --- a/valgrind/fjalar/fjalar_runtime.c +++ b/valgrind/fjalar/fjalar_runtime.c @@ -190,6 +190,8 @@ returnArrayVariableWithAddr(VarList* varList, 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) { @@ -223,6 +225,64 @@ returnArrayVariableWithAddr(VarList* varList, potentialVarBaseAddr = e->lowSP + potentialVar->byteOffset; } } + // FJALAR_DPRINTF("potential var expression size: %u\n", potentialVar->location_expression_size); + 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; + Addr 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]: %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). + reg_val = e->lowSP; + } else { + reg_val = (*get_reg[dloc->atom - DW_OP_breg0])(tid); + } + 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. + 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: %u, varType: %u\n", (void*)a, (void*)potentialVarBaseAddr, potentialVar->staticArr, potentialVar->ptrLevels, (potentialVar->varType ? potentialVar->varType->decType : 0)); diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index 2ab15396..a2030242 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -852,25 +852,6 @@ void visitClassMemberVariables(VisitArgs* args) { FJALAR_DPRINTF("Exit visitClassMemberVariables\n"); } -// Returns true if get_reg can read DWARF register regno. -static int dwarf_reg_is_readable(unsigned int regno) { - return ((regno < sizeof(get_reg) / sizeof(get_reg[0])) - && (get_reg[regno] != NULL)); -} - -// A register location (DW_OP_reg*) is not supported: visitVariableGroup -// reads a variable from guest memory, and a register has no address, no -// A and V bits, and no DynComp tags. -int location_op_is_supported(unsigned int op) { - if ((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { - return dwarf_reg_is_readable(op - DW_OP_breg0); - } - return ((op == DW_OP_deref) - || ((op >= DW_OP_const1u) && (op <= DW_OP_consts)) - || (op == DW_OP_plus_uconst) - || (op == DW_OP_fbreg)); -} - // Visits an entire group of variables, depending on the value of varOrigin: // If varOrigin == GLOBAL_VAR, then visit all global variables // If varOrigin == FUNCTION_FORMAL_PARAM, then visit all formal parameters @@ -1027,13 +1008,16 @@ 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_deref) { + 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; @@ -1042,10 +1026,17 @@ void visitVariableGroup(VariableOrigin varOrigin, // DWARF supplied constant var_loc = dloc->atom_offset; - } else if(op == DW_OP_plus_uconst) { + } 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]: %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) { @@ -1068,7 +1059,8 @@ void visitVariableGroup(VariableOrigin varOrigin, var_loc = stackBaseAddrGuest + dloc->atom_offset; } else { - // location_op_is_supported must agree with this evaluator. + // 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); } diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index 48ecd594..fc89f77d 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2562,30 +2562,24 @@ static void extractOneFormalParameterVar(FunctionEntry* f, 0,0,0,0,0,0,0,1, 0); - if (paramPtr->location_has_unsupported_op) { - // 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; + 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; + } - 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; + 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); - } + FJALAR_DPRINTF(" location_type: %u, byteOffset: %x\n", varPtr->locationType, (unsigned int)varPtr->byteOffset); } FJALAR_DPRINTF("EXIT extractOneFormalParameterVar\n"); @@ -3154,7 +3148,6 @@ 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); @@ -3166,7 +3159,6 @@ 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); @@ -3218,9 +3210,7 @@ static void initConstructorsAndDestructors(void) { // (comment added 2009) // RUDD - Dirty dirty hack. Force the location of this to be valid - if (!f->formalParameters.first->var->locationUnsupported) { - f->formalParameters.first->var->validLoc = 1; - } + f->formalParameters.first->var->validLoc = 1; // Use the regular (not mangled or demangled) name for matching diff --git a/valgrind/fjalar/tests/omitted-params-test.sh b/valgrind/fjalar/tests/omitted-params-test.sh index 549986a7..f8c9e528 100755 --- a/valgrind/fjalar/tests/omitted-params-test.sh +++ b/valgrind/fjalar/tests/omitted-params-test.sh @@ -20,29 +20,8 @@ trap 'rm -rf "${output_dir}"' EXIT # 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 -S -dA \ - -o "${output_dir}/omitted-params.s" "${test_dir}/omitted-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 x 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/ && /"x\\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 x: " $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 x" > "/dev/stderr"; exit 1 } }' \ - "${output_dir}/omitted-params.s" > "${output_dir}/omitted-params-patched.s" -gcc -gdwarf-4 -no-pie \ - -o "${output_dir}/omitted-params" "${output_dir}/omitted-params-patched.s" +gcc -gdwarf-4 -gno-variable-location-views -no-pie -O1 \ + -o "${output_dir}/omitted-params" "${test_dir}/omitted-params.c" decls="${output_dir}/omitted-params.decls" dtrace="${output_dir}/omitted-params.dtrace" diff --git a/valgrind/fjalar/tests/omitted-params.c b/valgrind/fjalar/tests/omitted-params.c index a053cb9f..ad4356c8 100644 --- a/valgrind/fjalar/tests/omitted-params.c +++ b/valgrind/fjalar/tests/omitted-params.c @@ -26,25 +26,13 @@ __attribute__((noinline)) void split(struct pair p) { sink = p.second; } -// d is in a register (DW_OP_reg*), which Fjalar does not support, so d is -// omitted. +// d is in a register, which Fjalar does not trace, so d is omitted. __attribute__((noinline)) double identity(double d) { return d; } -// Unoptimized, x is in the stack frame (DW_OP_fbreg). omitted-params-test.sh -// rewrites x's location to "DW_OP_fbreg -8; DW_OP_stack_value", which Fjalar -// does not support, so x is omitted. -__attribute__((noinline, optimize("O0"))) void stack_value(long x) { - // buf makes x's frame offset large enough to need 2 bytes. - volatile char buf[200]; - buf[0] = 1; - sink = x + buf[0]; -} - int main(void) { struct pair p = {5, 2}; split(p); - stack_value(7); return (int)identity(1.5) == 12345; } diff --git a/valgrind/fjalar/typedata.c b/valgrind/fjalar/typedata.c index f543ffcd..b96e81ee 100644 --- a/valgrind/fjalar/typedata.c +++ b/valgrind/fjalar/typedata.c @@ -1203,10 +1203,7 @@ 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; - if (paramPtr->dwarf_stack_size == MAX_DWARF_OPS) { - paramPtr->location_has_unsupported_op = 1; - return 1; - } + tl_assert(paramPtr->dwarf_stack_size < MAX_DWARF_OPS); paramPtr->dwarf_stack[paramPtr->dwarf_stack_size].atom = atom; paramPtr->dwarf_stack[paramPtr->dwarf_stack_size].atom_offset = value; paramPtr->dwarf_stack_size++; @@ -1237,20 +1234,6 @@ char harvest_formal_param_location_offset(dwarf_entry* e, long value) return 0; } -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; -} - char harvest_data_member_location(dwarf_entry* e, unsigned long value) { unsigned long tag; @@ -1931,7 +1914,6 @@ 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->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 8208a83d..c49e9f3a 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -347,13 +347,6 @@ 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 DW_OP_reg*, - // DW_OP_piece, or DW_OP_stack_value, or contains more than - // MAX_DWARF_OPS operations. - // Fjalar cannot read such a location. - char location_has_unsupported_op; - unsigned long abstract_origin_ID; // See comment in the function struct definition // for the uses of this. @@ -575,7 +568,6 @@ 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_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); From 6f84728047ffab0d145cfd10c98a0eb10337ced4 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 17:03:52 -0700 Subject: [PATCH 14/23] Omit a formal parameter whose location contains an unsupported DWARF op location_op_is_supported() is the single definition of which DWARF ops Fjalar's evaluator can apply. A location with any other op, such as DW_OP_reg*, DW_OP_piece, or DW_OP_stack_value, or with more than MAX_DWARF_OPS ops, is unreadable, and the this and main argc/argv fix-ups do not force it valid. DW_OP_const1u, DW_OP_const1s, DW_OP_const8u, and DW_OP_const8s are recorded like the other constants. --- valgrind/fjalar/dwarf.c | 39 ++++++++++++++---- valgrind/fjalar/fjalar_dwarf.h | 5 +++ valgrind/fjalar/fjalar_include.h | 5 +++ valgrind/fjalar/fjalar_traversal.c | 38 +++++++++++------- valgrind/fjalar/generate_fjalar_entries.c | 42 ++++++++++++-------- valgrind/fjalar/tests/omitted-params-test.sh | 25 +++++++++++- valgrind/fjalar/tests/omitted-params.c | 14 ++++++- valgrind/fjalar/typedata.c | 20 +++++++++- valgrind/fjalar/typedata.h | 8 ++++ 9 files changed, 153 insertions(+), 43 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index 080057cd..45fff718 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1451,6 +1451,13 @@ decode_location_expression (unsigned char * data, if(ll) {ll->atom = op;} + /* An op that Fjalar's evaluator cannot apply, such as DW_OP_reg*, + DW_OP_piece, DW_OP_stack_value, or DW_OP_plus (the evaluator has + no operand stack), makes a formal parameter's location + unreadable. */ + if (ok_to_harvest && op != DW_OP_nop && !location_op_is_supported (op)) + harvest_formal_param_location_unsupported (entry); + switch (op) { case DW_OP_addr: @@ -1469,10 +1476,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 +1529,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); diff --git a/valgrind/fjalar/fjalar_dwarf.h b/valgrind/fjalar/fjalar_dwarf.h index f3e97557..5ebb7680 100644 --- a/valgrind/fjalar/fjalar_dwarf.h +++ b/valgrind/fjalar/fjalar_dwarf.h @@ -41,4 +41,9 @@ 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); +// 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..98089c9f 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 diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index a2030242..2ab15396 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -852,6 +852,25 @@ void visitClassMemberVariables(VisitArgs* args) { FJALAR_DPRINTF("Exit visitClassMemberVariables\n"); } +// Returns true if get_reg can read DWARF register regno. +static int dwarf_reg_is_readable(unsigned int regno) { + return ((regno < sizeof(get_reg) / sizeof(get_reg[0])) + && (get_reg[regno] != NULL)); +} + +// A register location (DW_OP_reg*) is not supported: visitVariableGroup +// reads a variable from guest memory, and a register has no address, no +// A and V bits, and no DynComp tags. +int location_op_is_supported(unsigned int op) { + if ((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { + return dwarf_reg_is_readable(op - DW_OP_breg0); + } + return ((op == DW_OP_deref) + || ((op >= DW_OP_const1u) && (op <= DW_OP_consts)) + || (op == DW_OP_plus_uconst) + || (op == DW_OP_fbreg)); +} + // Visits an entire group of variables, depending on the value of varOrigin: // If varOrigin == GLOBAL_VAR, then visit all global variables // If varOrigin == FUNCTION_FORMAL_PARAM, then visit all formal parameters @@ -1008,16 +1027,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; - - } 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; @@ -1026,17 +1042,10 @@ 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) { @@ -1059,8 +1068,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); } diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index fc89f77d..48ecd594 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2562,24 +2562,30 @@ 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) { + // 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; + 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); + FJALAR_DPRINTF(" location_type: %u, byteOffset: %x\n", varPtr->locationType, (unsigned int)varPtr->byteOffset); + } } FJALAR_DPRINTF("EXIT extractOneFormalParameterVar\n"); @@ -3148,6 +3154,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 +3166,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 +3218,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/tests/omitted-params-test.sh b/valgrind/fjalar/tests/omitted-params-test.sh index f8c9e528..549986a7 100755 --- a/valgrind/fjalar/tests/omitted-params-test.sh +++ b/valgrind/fjalar/tests/omitted-params-test.sh @@ -20,8 +20,29 @@ trap 'rm -rf "${output_dir}"' EXIT # 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 \ - -o "${output_dir}/omitted-params" "${test_dir}/omitted-params.c" +gcc -gdwarf-4 -gno-variable-location-views -no-pie -O1 -S -dA \ + -o "${output_dir}/omitted-params.s" "${test_dir}/omitted-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 x 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/ && /"x\\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 x: " $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 x" > "/dev/stderr"; exit 1 } }' \ + "${output_dir}/omitted-params.s" > "${output_dir}/omitted-params-patched.s" +gcc -gdwarf-4 -no-pie \ + -o "${output_dir}/omitted-params" "${output_dir}/omitted-params-patched.s" decls="${output_dir}/omitted-params.decls" dtrace="${output_dir}/omitted-params.dtrace" diff --git a/valgrind/fjalar/tests/omitted-params.c b/valgrind/fjalar/tests/omitted-params.c index ad4356c8..a053cb9f 100644 --- a/valgrind/fjalar/tests/omitted-params.c +++ b/valgrind/fjalar/tests/omitted-params.c @@ -26,13 +26,25 @@ __attribute__((noinline)) void split(struct pair p) { sink = p.second; } -// d is in a register, which Fjalar does not trace, so d is omitted. +// d is in a register (DW_OP_reg*), which Fjalar does not support, so d is +// omitted. __attribute__((noinline)) double identity(double d) { return d; } +// Unoptimized, x is in the stack frame (DW_OP_fbreg). omitted-params-test.sh +// rewrites x's location to "DW_OP_fbreg -8; DW_OP_stack_value", which Fjalar +// does not support, so x is omitted. +__attribute__((noinline, optimize("O0"))) void stack_value(long x) { + // buf makes x's frame offset large enough to need 2 bytes. + volatile char buf[200]; + buf[0] = 1; + sink = x + buf[0]; +} + int main(void) { struct pair p = {5, 2}; split(p); + stack_value(7); return (int)identity(1.5) == 12345; } diff --git a/valgrind/fjalar/typedata.c b/valgrind/fjalar/typedata.c index b96e81ee..f543ffcd 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,20 @@ char harvest_formal_param_location_offset(dwarf_entry* e, long value) return 0; } +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; +} + char harvest_data_member_location(dwarf_entry* e, unsigned long value) { unsigned long tag; @@ -1914,6 +1931,7 @@ 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->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..8208a83d 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -347,6 +347,13 @@ 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 DW_OP_reg*, + // DW_OP_piece, or DW_OP_stack_value, or contains more than + // MAX_DWARF_OPS operations. + // Fjalar cannot read such a location. + char location_has_unsupported_op; + unsigned long abstract_origin_ID; // See comment in the function struct definition // for the uses of this. @@ -568,6 +575,7 @@ 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_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); From f0169456c133e086d2db5c063139fac05555bda9 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 17:04:35 -0700 Subject: [PATCH 15/23] Remove an unused copy of the location evaluator returnArrayVariableWithAddr evaluated a formal parameter's DWARF location expression into var_loc but never used the result. --- valgrind/fjalar/fjalar_runtime.c | 60 -------------------------------- 1 file changed, 60 deletions(-) diff --git a/valgrind/fjalar/fjalar_runtime.c b/valgrind/fjalar/fjalar_runtime.c index f910808e..a60347a4 100644 --- a/valgrind/fjalar/fjalar_runtime.c +++ b/valgrind/fjalar/fjalar_runtime.c @@ -190,8 +190,6 @@ returnArrayVariableWithAddr(VarList* varList, 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) { @@ -225,64 +223,6 @@ returnArrayVariableWithAddr(VarList* varList, potentialVarBaseAddr = e->lowSP + potentialVar->byteOffset; } } - // FJALAR_DPRINTF("potential var expression size: %u\n", potentialVar->location_expression_size); - 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; - Addr 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]: %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). - reg_val = e->lowSP; - } else { - reg_val = (*get_reg[dloc->atom - DW_OP_breg0])(tid); - } - 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. - 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: %u, varType: %u\n", (void*)a, (void*)potentialVarBaseAddr, potentialVar->staticArr, potentialVar->ptrLevels, (potentialVar->varType ? potentialVar->varType->decType : 0)); From 1f7e40409b8fafbd877be9ca5ebc059adcea7b76 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 17:26:48 -0700 Subject: [PATCH 16/23] Rename a stale debug-message label --- valgrind/fjalar/fjalar_runtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/valgrind/fjalar/fjalar_runtime.c b/valgrind/fjalar/fjalar_runtime.c index a60347a4..1d6a1e6b 100644 --- a/valgrind/fjalar/fjalar_runtime.c +++ b/valgrind/fjalar/fjalar_runtime.c @@ -223,7 +223,7 @@ returnArrayVariableWithAddr(VarList* varList, potentialVarBaseAddr = e->lowSP + potentialVar->byteOffset; } } - FJALAR_DPRINTF("addr: %p, potential var_loc: %p, staticArr: %p, ptrLevels: %u, varType: %u\n", + FJALAR_DPRINTF("addr: %p, potentialVarBaseAddr: %p, staticArr: %p, ptrLevels: %u, varType: %u\n", (void*)a, (void*)potentialVarBaseAddr, potentialVar->staticArr, potentialVar->ptrLevels, (potentialVar->varType ? potentialVar->varType->decType : 0)); From 30557bada36be7d34299f3e00305d5f5ef96f135 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 17:41:57 -0700 Subject: [PATCH 17/23] Document dwarf_op_reg_num at its definition --- valgrind/fjalar/fjalar_main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index a379bc7b..33a2addd 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -172,6 +172,11 @@ Addr read_dwarf_reg(ThreadId tid, UInt regNum) 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) { From 21ecdf5b1cbf528067cc58d0a59302ab8f169706 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 17:50:40 -0700 Subject: [PATCH 18/23] Omit parameters with location lists; check the .dtrace file in the test - Treat a formal parameter whose location is a location list as unsupported. - Move location_op_is_supported to fjalar_dwarf.c, so the DWARF parser does not depend on the traversal code or on get_reg. - Explain why register locations are unsupported. - Remove a dead location evaluator from returnArrayVariableWithAddr. - Test: match program points whose names contain underscores, and add a formal parameter that Fjalar must not omit. --- valgrind/fjalar/dwarf.c | 4 ++ valgrind/fjalar/fjalar_dwarf.c | 18 ++++++ valgrind/fjalar/fjalar_dwarf.h | 8 +++ valgrind/fjalar/fjalar_main.c | 2 + valgrind/fjalar/fjalar_runtime.c | 62 +------------------- valgrind/fjalar/fjalar_traversal.c | 19 ------ valgrind/fjalar/tests/omitted-params-test.sh | 6 +- valgrind/fjalar/tests/omitted-params.c | 10 +++- valgrind/fjalar/tests/omitted-params.goal | 4 ++ 9 files changed, 49 insertions(+), 84 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index 45fff718..f005d07d 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -3824,6 +3824,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"); diff --git a/valgrind/fjalar/fjalar_dwarf.c b/valgrind/fjalar/fjalar_dwarf.c index f5450e25..3f904797 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 } } + +// Register locations (DW_OP_reg*) are not supported, so Fjalar omits a +// formal parameter whose value is in a register. visitVariableGroup and +// everything downstream of it treat a variable as an address in guest +// memory: they read the value from that address and look up its A and V +// bits and its DynComp tags in shadow memory. A register has no such +// address. Supporting registers would require reading the guest register +// and its shadow state (VG_(get_shadow_regs_area)) and tracking DynComp tags +// for registers. +int location_op_is_supported(unsigned int op) { + if ((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { + return (op - DW_OP_breg0) < NUM_READABLE_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 5ebb7680..67791752 100644 --- a/valgrind/fjalar/fjalar_dwarf.h +++ b/valgrind/fjalar/fjalar_dwarf.h @@ -41,6 +41,14 @@ 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); +// The number of DWARF registers, starting at 0, that get_reg (in +// fjalar_main.c) can read. +#if defined(VGA_amd64) +#define NUM_READABLE_DWARF_REGS 16 +#else +#define NUM_READABLE_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. diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index c0066a3c..b0f5e937 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -155,6 +155,8 @@ Addr (*get_reg[11])( ThreadId tid ) = { NULL }; #endif +// Entries 0 through NUM_READABLE_DWARF_REGS - 1 of get_reg are non-NULL. +STATIC_ASSERT(NUM_READABLE_DWARF_REGS <= sizeof(get_reg) / sizeof(get_reg[0])); // For debugging purposes, a mapping between // DWARF location atoms and their string diff --git a/valgrind/fjalar/fjalar_runtime.c b/valgrind/fjalar/fjalar_runtime.c index f910808e..1d6a1e6b 100644 --- a/valgrind/fjalar/fjalar_runtime.c +++ b/valgrind/fjalar/fjalar_runtime.c @@ -190,8 +190,6 @@ returnArrayVariableWithAddr(VarList* varList, 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) { @@ -225,65 +223,7 @@ returnArrayVariableWithAddr(VarList* varList, potentialVarBaseAddr = e->lowSP + potentialVar->byteOffset; } } - // FJALAR_DPRINTF("potential var expression size: %u\n", potentialVar->location_expression_size); - 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; - Addr 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]: %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). - reg_val = e->lowSP; - } else { - reg_val = (*get_reg[dloc->atom - DW_OP_breg0])(tid); - } - 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. - 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: %u, varType: %u\n", + FJALAR_DPRINTF("addr: %p, potentialVarBaseAddr: %p, staticArr: %p, ptrLevels: %u, varType: %u\n", (void*)a, (void*)potentialVarBaseAddr, potentialVar->staticArr, potentialVar->ptrLevels, (potentialVar->varType ? potentialVar->varType->decType : 0)); diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index 2ab15396..683e029f 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -852,25 +852,6 @@ void visitClassMemberVariables(VisitArgs* args) { FJALAR_DPRINTF("Exit visitClassMemberVariables\n"); } -// Returns true if get_reg can read DWARF register regno. -static int dwarf_reg_is_readable(unsigned int regno) { - return ((regno < sizeof(get_reg) / sizeof(get_reg[0])) - && (get_reg[regno] != NULL)); -} - -// A register location (DW_OP_reg*) is not supported: visitVariableGroup -// reads a variable from guest memory, and a register has no address, no -// A and V bits, and no DynComp tags. -int location_op_is_supported(unsigned int op) { - if ((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { - return dwarf_reg_is_readable(op - DW_OP_breg0); - } - return ((op == DW_OP_deref) - || ((op >= DW_OP_const1u) && (op <= DW_OP_consts)) - || (op == DW_OP_plus_uconst) - || (op == DW_OP_fbreg)); -} - // Visits an entire group of variables, depending on the value of varOrigin: // If varOrigin == GLOBAL_VAR, then visit all global variables // If varOrigin == FUNCTION_FORMAL_PARAM, then visit all formal parameters diff --git a/valgrind/fjalar/tests/omitted-params-test.sh b/valgrind/fjalar/tests/omitted-params-test.sh index 549986a7..d51389bf 100755 --- a/valgrind/fjalar/tests/omitted-params-test.sh +++ b/valgrind/fjalar/tests/omitted-params-test.sh @@ -67,9 +67,9 @@ awk '/^ppt / { ppt = $2 } /^ variable / { if ($2 !~ /^::/ && ppt !~ /^\.\.main\(/) print "decls", ppt, $2 }' \ "${decls}" > "${actual}" -awk '/^\.\.[a-z]*\(\):::/ { ppt = $0; getline; getline - if (ppt ~ /^\.\.main\(/ || seen[ppt]++) ppt = "" - next } +awk '/^\.\.[A-Za-z0-9_]*\(\):::/ { ppt = $0; getline; getline + if (ppt ~ /^\.\.main\(/ || seen[ppt]++) ppt = "" + next } ppt == "" { next } /^$/ { ppt = ""; next } { var = $0; getline; getline diff --git a/valgrind/fjalar/tests/omitted-params.c b/valgrind/fjalar/tests/omitted-params.c index a053cb9f..26db09c6 100644 --- a/valgrind/fjalar/tests/omitted-params.c +++ b/valgrind/fjalar/tests/omitted-params.c @@ -13,7 +13,8 @@ // A C program that, when compiled with optimization, has formal parameters // whose locations Fjalar cannot read. Fjalar omits them from both the .decls -// and .dtrace files. See omitted-params-test.sh. +// and .dtrace files. It also has a formal parameter that Fjalar can read, to +// check that Fjalar does not omit every parameter. See omitted-params-test.sh. struct pair { long first; long second; }; @@ -42,9 +43,16 @@ __attribute__((noinline, optimize("O0"))) void stack_value(long x) { sink = x + buf[0]; } +// Unoptimized, y is in the stack frame (DW_OP_fbreg), which Fjalar supports, +// so y is not omitted. +__attribute__((noinline, optimize("O0"))) void kept(long y) { + sink = y; +} + int main(void) { struct pair p = {5, 2}; split(p); stack_value(7); + kept(3); return (int)identity(1.5) == 12345; } diff --git a/valgrind/fjalar/tests/omitted-params.goal b/valgrind/fjalar/tests/omitted-params.goal index e626e6b3..8ccf5e95 100644 --- a/valgrind/fjalar/tests/omitted-params.goal +++ b/valgrind/fjalar/tests/omitted-params.goal @@ -1,2 +1,6 @@ +decls ..kept():::ENTER y +decls ..kept():::EXIT0 y decls ..identity():::EXIT0 return +dtrace ..kept():::ENTER y +dtrace ..kept():::EXIT0 y dtrace ..identity():::EXIT0 return From 9d37c9eedd32aca31e1bd9dfe1731a193a1a5483 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 17:53:56 -0700 Subject: [PATCH 19/23] Support a location that is one whole piece; sort test output - A formal parameter whose location ends with one DW_OP_piece that covers the whole parameter is no longer omitted. - Check at startup that get_reg can read every register that location_op_is_supported accepts. - Test: sort the output, because the order of program points in the .decls file depends on a hash table; test a whole piece and a partial piece. --- valgrind/fjalar/dwarf.c | 20 ++++++++-- valgrind/fjalar/fjalar_main.c | 8 +++- valgrind/fjalar/generate_fjalar_entries.c | 6 ++- valgrind/fjalar/tests/omitted-params-test.sh | 42 +++++++++++++++++--- valgrind/fjalar/tests/omitted-params.c | 15 +++++++ valgrind/fjalar/tests/omitted-params.goal | 8 +++- valgrind/fjalar/typedata.c | 15 +++++++ valgrind/fjalar/typedata.h | 10 ++++- 8 files changed, 108 insertions(+), 16 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index f005d07d..239ecd59 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1452,10 +1452,11 @@ decode_location_expression (unsigned char * data, if(ll) {ll->atom = op;} /* An op that Fjalar's evaluator cannot apply, such as DW_OP_reg*, - DW_OP_piece, DW_OP_stack_value, or DW_OP_plus (the evaluator has - no operand stack), makes a formal parameter's location - unreadable. */ - if (ok_to_harvest && op != DW_OP_nop && !location_op_is_supported (op)) + 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) @@ -1832,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. */ + if (ok_to_harvest) + { + if (data == end) + 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: diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index b0f5e937..68d40e78 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -155,7 +155,8 @@ Addr (*get_reg[11])( ThreadId tid ) = { NULL }; #endif -// Entries 0 through NUM_READABLE_DWARF_REGS - 1 of get_reg are non-NULL. +// Entries 0 through NUM_READABLE_DWARF_REGS - 1 of get_reg are non-NULL; +// fjalar_pre_clo_init checks that. STATIC_ASSERT(NUM_READABLE_DWARF_REGS <= sizeof(get_reg) / sizeof(get_reg[0])); // For debugging purposes, a mapping between @@ -1033,6 +1034,11 @@ static void outputAuxiliaryFilesAndExit(void) { // This is called before command-line options are processed void fjalar_pre_clo_init() { + unsigned int regno; + for (regno = 0; regno < NUM_READABLE_DWARF_REGS; regno++) { + tl_assert(get_reg[regno] != NULL); + } + fn_stack_first_free_index = VG_(malloc)("fjalar_main.c: fjalar_pre_clo_init1", VG_N_THREADS * sizeof fn_stack_first_free_index[0]); FunctionExecutionStateStack = VG_(malloc)("fjalar_main.c: fjalar_pre_clo_init2", VG_N_THREADS * FN_STACK_SIZE * sizeof FunctionExecutionStateStack[0][0]); diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index 48ecd594..df17693e 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2562,7 +2562,11 @@ static void extractOneFormalParameterVar(FunctionEntry* f, 0,0,0,0,0,0,0,1, 0); - if (paramPtr->location_has_unsupported_op) { + 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; diff --git a/valgrind/fjalar/tests/omitted-params-test.sh b/valgrind/fjalar/tests/omitted-params-test.sh index d51389bf..16b51749 100755 --- a/valgrind/fjalar/tests/omitted-params-test.sh +++ b/valgrind/fjalar/tests/omitted-params-test.sh @@ -40,7 +40,34 @@ awk '/DW_AT_name/ && /"x\\0"/ && !done { state = 1 } } { print } END { if (!done) { print "did not find x" > "/dev/stderr"; exit 1 } }' \ - "${output_dir}/omitted-params.s" > "${output_dir}/omitted-params-patched.s" + "${output_dir}/omitted-params.s" > "${output_dir}/omitted-params-x.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}/omitted-params-x.s" \ + | append_piece p__ p 4 > "${output_dir}/omitted-params-patched.s" gcc -gdwarf-4 -no-pie \ -o "${output_dir}/omitted-params" "${output_dir}/omitted-params-patched.s" @@ -59,14 +86,16 @@ if [ "${status}" -ne 0 ]; then fi # One line per non-global variable declared at each program point other than -# main's: "decls ", followed by one line per non-global -# variable at the first occurrence of each program point other than main's: -# "dtrace ". +# main's: "decls ", and one line per non-global variable at +# the first occurrence of each program point other than main's: +# "dtrace ". The lines are sorted, because the order of +# program points in the .decls file depends on Fjalar's hash table. actual="${output_dir}/omitted-params.actual" +{ awk '/^ppt / { ppt = $2 } /^ variable / { if ($2 !~ /^::/ && ppt !~ /^\.\.main\(/) print "decls", ppt, $2 }' \ - "${decls}" > "${actual}" + "${decls}" awk '/^\.\.[A-Za-z0-9_]*\(\):::/ { ppt = $0; getline; getline if (ppt ~ /^\.\.main\(/ || seen[ppt]++) ppt = "" next } @@ -74,7 +103,8 @@ awk '/^\.\.[A-Za-z0-9_]*\(\):::/ { ppt = $0; getline; getline /^$/ { ppt = ""; next } { var = $0; getline; getline if (var !~ /^::/) print "dtrace", ppt, var }' \ - "${dtrace}" >> "${actual}" + "${dtrace}" +} | LC_ALL=C sort > "${actual}" if ! diff -u "${test_dir}/omitted-params.goal" "${actual}" >&2; then echo "$0: FAILED: output differs from omitted-params.goal" >&2 diff --git a/valgrind/fjalar/tests/omitted-params.c b/valgrind/fjalar/tests/omitted-params.c index 26db09c6..30ed2764 100644 --- a/valgrind/fjalar/tests/omitted-params.c +++ b/valgrind/fjalar/tests/omitted-params.c @@ -49,10 +49,25 @@ __attribute__((noinline, optimize("O0"))) void kept(long y) { sink = y; } +// Unoptimized, w__ and p__ are in the stack frame (DW_OP_fbreg). +// omitted-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. +__attribute__((noinline, optimize("O0"))) void whole_piece(long w__) { + sink = w__; +} + +__attribute__((noinline, optimize("O0"))) void part_piece(long p__) { + sink = p__; +} + int main(void) { struct pair p = {5, 2}; split(p); stack_value(7); kept(3); + whole_piece(4); + part_piece(5); return (int)identity(1.5) == 12345; } diff --git a/valgrind/fjalar/tests/omitted-params.goal b/valgrind/fjalar/tests/omitted-params.goal index 8ccf5e95..41216883 100644 --- a/valgrind/fjalar/tests/omitted-params.goal +++ b/valgrind/fjalar/tests/omitted-params.goal @@ -1,6 +1,10 @@ +decls ..identity():::EXIT0 return decls ..kept():::ENTER y decls ..kept():::EXIT0 y -decls ..identity():::EXIT0 return +decls ..whole_piece():::ENTER w +decls ..whole_piece():::EXIT0 w +dtrace ..identity():::EXIT0 return dtrace ..kept():::ENTER y dtrace ..kept():::EXIT0 y -dtrace ..identity():::EXIT0 return +dtrace ..whole_piece():::ENTER w +dtrace ..whole_piece():::EXIT0 w diff --git a/valgrind/fjalar/typedata.c b/valgrind/fjalar/typedata.c index f543ffcd..55388793 100644 --- a/valgrind/fjalar/typedata.c +++ b/valgrind/fjalar/typedata.c @@ -1251,6 +1251,20 @@ char harvest_formal_param_location_unsupported(dwarf_entry* e) return 0; } +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; @@ -1932,6 +1946,7 @@ static void process_abstract_origin_items(void) 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 8208a83d..c9a43afc 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -349,11 +349,16 @@ typedef struct // True if the location expression contains an operation that does // not satisfy location_op_is_supported, such as DW_OP_reg*, - // DW_OP_piece, or DW_OP_stack_value, or contains more than - // MAX_DWARF_OPS operations. + // DW_OP_stack_value, or a DW_OP_piece that is not the last operation, + // 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. @@ -576,6 +581,7 @@ 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); From 8f675565ed0bad1f568970113b222a09b2bff508 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 17:59:21 -0700 Subject: [PATCH 20/23] Document harvest_formal_param_location_unsupported and harvest_formal_param_location_piece --- valgrind/fjalar/typedata.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/valgrind/fjalar/typedata.c b/valgrind/fjalar/typedata.c index 55388793..ec8228e0 100644 --- a/valgrind/fjalar/typedata.c +++ b/valgrind/fjalar/typedata.c @@ -1237,6 +1237,10 @@ 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)) @@ -1251,6 +1255,10 @@ char harvest_formal_param_location_unsupported(dwarf_entry* e) 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)) From 8018d10bde894f1e2239d10185e89492c53bef7c Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 19:23:50 -0700 Subject: [PATCH 21/23] Save only the registers that valid parameter locations use; give no stack slot to any parameter in a register From #93: * Record in FunctionEntry.entryRegsMask the readable registers that valid formal-parameter locations use, and save only those at function entrance. * A parameter whose location is a register occupies no stack slot, even if Fjalar cannot read the register. * Remove a dead REGISTER_LOCATION check from returnArrayVariableWithAddr, which never sees formal parameters. * Fold omitted-params-test.sh into register-params-test.sh, which now checks every .decls program point, sorted. --- Makefile | 1 - valgrind/fjalar/fjalar_include.h | 17 ++--- valgrind/fjalar/fjalar_main.c | 16 ++--- valgrind/fjalar/fjalar_runtime.c | 5 -- valgrind/fjalar/fjalar_traversal.c | 2 +- valgrind/fjalar/generate_fjalar_entries.c | 48 ++++++++++---- valgrind/fjalar/tests/omitted-params-test.sh | 63 ------------------- valgrind/fjalar/tests/omitted-params.c | 39 ------------ valgrind/fjalar/tests/omitted-params.goal | 2 - valgrind/fjalar/tests/register-params-test.sh | 13 ++-- valgrind/fjalar/tests/register-params.goal | 10 ++- 11 files changed, 68 insertions(+), 148 deletions(-) delete mode 100755 valgrind/fjalar/tests/omitted-params-test.sh delete mode 100644 valgrind/fjalar/tests/omitted-params.c delete mode 100644 valgrind/fjalar/tests/omitted-params.goal diff --git a/Makefile b/Makefile index 764f9459..55684f29 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,6 @@ test: # Fjalar tests that do not depend on Daikon fjalar-test: valgrind/fjalar/tests/library-compilation-unit-test.sh - valgrind/fjalar/tests/omitted-params-test.sh valgrind/fjalar/tests/register-params-test.sh # Kvasir tests diff --git a/valgrind/fjalar/fjalar_include.h b/valgrind/fjalar/fjalar_include.h index b8e5639d..3a711d04 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -690,10 +690,11 @@ typedef struct _FunctionEntry { UInt nonce; - // True if some formal parameter's location expression uses a - // register (DW_OP_reg* or DW_OP_breg*). If so, Fjalar saves the - // registers at function entrance in FunctionExecutionState.entryRegs. - Bool savesEntryRegs; + // 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; @@ -836,17 +837,17 @@ typedef struct { 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 - // dwarf_reg_is_readable(N). A formal parameter whose location is a + // 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 the readable registers, with their A and V bits and + // 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->savesEntryRegs is false. + // after it; it is null if func->entryRegsMask is 0. Addr* entryRegs; diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index 97cb46f4..4e3257d9 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -192,15 +192,15 @@ Bool dwarf_op_reg_num(UInt atom, UInt* regNum) return False; } -// Saves the readable registers, 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. +// 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 (dwarf_reg_is_readable(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]); @@ -767,14 +767,14 @@ void enter_function(FunctionEntry* f) if (size != 0) { // entryRegs follows the virtual stack in the same allocation. Int entryRegsOffset = VG_ROUNDUP(size, sizeof(Addr)); - Int entryRegsSize = (f->savesEntryRegs + 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->savesEntryRegs + newEntry->entryRegs = (f->entryRegsMask != 0 ? (Addr*)(newEntry->virtualStack + entryRegsOffset) : NULL); newEntry->virtualStackByteSize = size; @@ -801,7 +801,7 @@ void enter_function(FunctionEntry* f) newEntry->func->guestStackEnd = newEntry->func->guestStackStart + size; newEntry->func->lowestVirtSP = (Addr)newEntry->virtualStack; - if (f->savesEntryRegs) { + if (f->entryRegsMask != 0) { save_param_regs(newEntry, tid); } } diff --git a/valgrind/fjalar/fjalar_runtime.c b/valgrind/fjalar/fjalar_runtime.c index b803a8dd..1d6a1e6b 100644 --- a/valgrind/fjalar/fjalar_runtime.c +++ b/valgrind/fjalar/fjalar_runtime.c @@ -208,11 +208,6 @@ returnArrayVariableWithAddr(VarList* varList, FJALAR_DPRINTF("Examining potential var: %s, offset: 0x%x, locType: 0x%x\n", potentialVar->name, (unsigned int) potentialVar->byteOffset, potentialVar->locationType); - // A formal parameter located in a register is not in memory, so - // it cannot contain a. - if (potentialVar->locationType == REGISTER_LOCATION) - continue; - if (isGlobal) { tl_assert(IS_GLOBAL_VAR(potentialVar)); potentialVarBaseAddr = potentialVar->globalVar->globalLocation; diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index 94464e76..ab7b5899 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -1039,7 +1039,7 @@ void visitVariableGroup(VariableOrigin varOrigin, // have overwritten the register and the epilogue has // already restored or popped it. Bool isBreg = (op >= DW_OP_breg0) && (op <= DW_OP_breg31); - if (!entryRegs || !dwarf_reg_is_readable(regNum)) { + if (!entryRegs || !(funcPtr->entryRegsMask & (1U << regNum))) { // extractOneFormalParameterVar() did not accept the // register location, so the register was not saved, // but some heuristic marked the location valid anyway. diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index a6007971..75aa5b39 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,8 +2418,9 @@ 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. - if (cur_node->var->locationType == REGISTER_LOCATION) { + // 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) { @@ -2546,6 +2562,21 @@ static Bool isSupportedRegisterLocation(formal_parameter* paramPtr, 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; + } + } +} + // dwarfParamEntry->tag_name == DW_TAG_formal_parameter static void extractOneFormalParameterVar(FunctionEntry* f, dwarf_entry* dwarfParamEntry) @@ -2603,17 +2634,6 @@ static void extractOneFormalParameterVar(FunctionEntry* f, varPtr->location_expression_size = paramPtr->dwarf_stack_size; } - { - unsigned int i; - for (i = 0; i < paramPtr->dwarf_stack_size; i++) { - UInt atom = paramPtr->dwarf_stack[i].atom; - if ((atom >= DW_OP_reg0 && atom <= DW_OP_reg31) || - (atom >= DW_OP_breg0 && atom <= DW_OP_breg31)) { - f->savesEntryRegs = True; - } - } - } - if (paramPtr->location_has_unsupported_op) { // varPtr->validLoc remains 0, so Fjalar omits the formal parameter. FJALAR_DPRINTF(" location contains an unsupported operation\n"); @@ -2622,12 +2642,14 @@ static void extractOneFormalParameterVar(FunctionEntry* f, 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)); } diff --git a/valgrind/fjalar/tests/omitted-params-test.sh b/valgrind/fjalar/tests/omitted-params-test.sh deleted file mode 100755 index f8c9e528..00000000 --- a/valgrind/fjalar/tests/omitted-params-test.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash - -# 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 \ - -o "${output_dir}/omitted-params" "${test_dir}/omitted-params.c" - -decls="${output_dir}/omitted-params.decls" -dtrace="${output_dir}/omitted-params.dtrace" -status=0 -"${valgrind}" --tool=fjalar \ - --decls-file="${decls}" --dtrace-file="${dtrace}" \ - "${output_dir}/omitted-params" \ - > "${output_dir}/omitted-params.out" 2>&1 || status=$? - -if [ "${status}" -ne 0 ]; then - echo "$0: FAILED: Fjalar exited with status ${status}" >&2 - cat "${output_dir}/omitted-params.out" >&2 - exit 1 -fi - -# One line per non-global variable declared at each program point other than -# main's: "decls ", followed by one line per non-global -# variable at the first occurrence of each program point other than main's: -# "dtrace ". -actual="${output_dir}/omitted-params.actual" -awk '/^ppt / { ppt = $2 } - /^ variable / { if ($2 !~ /^::/ && ppt !~ /^\.\.main\(/) - print "decls", ppt, $2 }' \ - "${decls}" > "${actual}" -awk '/^\.\.[a-z]*\(\):::/ { ppt = $0; getline; getline - if (ppt ~ /^\.\.main\(/ || seen[ppt]++) ppt = "" - next } - ppt == "" { next } - /^$/ { ppt = ""; next } - { var = $0; getline; getline - if (var !~ /^::/) print "dtrace", ppt, var }' \ - "${dtrace}" >> "${actual}" - -if ! diff -u "${test_dir}/omitted-params.goal" "${actual}" >&2; then - echo "$0: FAILED: output differs from omitted-params.goal" >&2 - exit 1 -fi - -echo "$0: PASSED" diff --git a/valgrind/fjalar/tests/omitted-params.c b/valgrind/fjalar/tests/omitted-params.c deleted file mode 100644 index ecd6a224..00000000 --- a/valgrind/fjalar/tests/omitted-params.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - 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 locations Fjalar cannot read. Fjalar omits them from both the .decls -// and .dtrace files. See omitted-params-test.sh. - -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. -__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. -__attribute__((noinline)) double identity(double d) { - return d; -} - -int main(void) { - struct pair p = {5, 2}; - split(p); - return (int)identity(1.5) == 12345; -} diff --git a/valgrind/fjalar/tests/omitted-params.goal b/valgrind/fjalar/tests/omitted-params.goal deleted file mode 100644 index e626e6b3..00000000 --- a/valgrind/fjalar/tests/omitted-params.goal +++ /dev/null @@ -1,2 +0,0 @@ -decls ..identity():::EXIT0 return -dtrace ..identity():::EXIT0 return diff --git a/valgrind/fjalar/tests/register-params-test.sh b/valgrind/fjalar/tests/register-params-test.sh index 8160f170..4c516c70 100755 --- a/valgrind/fjalar/tests/register-params-test.sh +++ b/valgrind/fjalar/tests/register-params-test.sh @@ -69,9 +69,12 @@ 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 at -# each exit program point other than main's: " comparability -# ". +# " ", 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 = "" @@ -84,9 +87,9 @@ awk '/^\.\.[a-z]*\(\):::/ { ppt = $0; getline; getline awk '/^ppt / { ppt = $2 } /^ variable / { var = $2 } var ~ /^::/ { next } - /^ comparability / { if (ppt ~ /:::EXIT/ && ppt !~ /^\.\.main\(/) + /^ comparability / { if (ppt !~ /^\.\.main\(/) print ppt, var, "comparability", $2 }' \ - "${decls}" >> "${actual}" + "${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 diff --git a/valgrind/fjalar/tests/register-params.goal b/valgrind/fjalar/tests/register-params.goal index 465c2be7..01d79210 100644 --- a/valgrind/fjalar/tests/register-params.goal +++ b/valgrind/fjalar/tests/register-params.goal @@ -9,10 +9,14 @@ ..scale():::EXIT0 factor 7 ..scale():::EXIT0 return 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 +..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 -..add():::EXIT0 a comparability 2 -..add():::EXIT0 b comparability 2 -..add():::EXIT0 return comparability 2 From 501b40985f38fd646b9207cc17594f8a61483940 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 19:59:07 -0700 Subject: [PATCH 22/23] Treat a final DW_OP_piece of size 0 as unsupported --- valgrind/fjalar/dwarf.c | 4 ++-- valgrind/fjalar/typedata.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index 239ecd59..85ab698e 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1836,10 +1836,10 @@ decode_location_expression (unsigned char * data, /* 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. */ + the parameter is split. A piece of size 0 covers no bytes. */ if (ok_to_harvest) { - if (data == end) + if (data == end && uvalue != 0) harvest_formal_param_location_piece (entry, uvalue); else harvest_formal_param_location_unsupported (entry); diff --git a/valgrind/fjalar/typedata.h b/valgrind/fjalar/typedata.h index c9a43afc..61a81f9a 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -349,7 +349,8 @@ typedef struct // True if the location expression contains an operation that does // not satisfy location_op_is_supported, such as DW_OP_reg*, - // DW_OP_stack_value, or a DW_OP_piece that is not the last operation, + // 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; From 4cfbe749f1560ba0caa79535db5bf21e71ac5d42 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 17 Oct 2026 08:45:55 -0700 Subject: [PATCH 23/23] Move the detection of unsupported location operations to #95 #95 replaces it with location_op_is_supported. Also leave FJALAR_NUM_DWARF_REGS in fjalar_main.c, where #91 defines it; #95 moves it to fjalar_dwarf.h. --- valgrind/fjalar/dwarf.c | 47 ++++------------------- valgrind/fjalar/fjalar_include.h | 8 ---- valgrind/fjalar/fjalar_main.c | 4 ++ valgrind/fjalar/fjalar_traversal.c | 2 +- valgrind/fjalar/generate_fjalar_entries.c | 5 +-- valgrind/fjalar/typedata.c | 22 ----------- valgrind/fjalar/typedata.h | 9 ----- 7 files changed, 14 insertions(+), 83 deletions(-) diff --git a/valgrind/fjalar/dwarf.c b/valgrind/fjalar/dwarf.c index 7c2e7059..f2aa4724 100644 --- a/valgrind/fjalar/dwarf.c +++ b/valgrind/fjalar/dwarf.c @@ -1447,11 +1447,6 @@ decode_location_expression (unsigned char * data, while (data < end) { - /* The size of a formal parameter's dwarf_stack before this op, or - -1 if not harvesting a formal parameter's location. */ - int param_stack_size_before - = (ok_to_harvest ? formal_param_location_stack_size (entry) : -1); - op = *data++; if(ll) {ll->atom = op;} @@ -1474,20 +1469,10 @@ 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: @@ -1527,22 +1512,16 @@ 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, 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)); + 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); break; case DW_OP_const8s: - 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)); + 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); break; case DW_OP_constu: READ_ULEB (uvalue, data, end); @@ -2095,20 +2074,10 @@ decode_location_expression (unsigned char * data, printf (_("(User defined location op 0x%x)"), op); else printf (_("(Unknown location op 0x%x)"), op); - if (param_stack_size_before >= 0) - harvest_formal_param_location_unsupported (entry); /* No way to tell where the next op is, so just bail. */ return need_frame_base; } - /* An op that is not in a formal parameter's dwarf_stack, such as - DW_OP_piece, DW_OP_stack_value, or DW_OP_plus (which Fjalar's - evaluator, having no operand stack, cannot apply), makes the - location one that Fjalar does not support. */ - if (param_stack_size_before >= 0 && op != DW_OP_nop - && formal_param_location_stack_size (entry) == param_stack_size_before) - harvest_formal_param_location_unsupported (entry); - /* Separate the ops. */ if (data < end) printf ("; "); diff --git a/valgrind/fjalar/fjalar_include.h b/valgrind/fjalar/fjalar_include.h index 3a711d04..f0a3ece5 100644 --- a/valgrind/fjalar/fjalar_include.h +++ b/valgrind/fjalar/fjalar_include.h @@ -754,14 +754,6 @@ traversals within data structures and arrays #define DWARF_FP_REG 5 #endif -// 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 - // 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); diff --git a/valgrind/fjalar/fjalar_main.c b/valgrind/fjalar/fjalar_main.c index 4e3257d9..623e6069 100644 --- a/valgrind/fjalar/fjalar_main.c +++ b/valgrind/fjalar/fjalar_main.c @@ -120,6 +120,9 @@ const HChar* executable_filename = 0; */ #if defined(VGA_amd64) +// One more than the largest DWARF register number that Fjalar can +// read. +#define FJALAR_NUM_DWARF_REGS 16 // AMD64 Dwarf to Architecture mapping is (thankfully) specified // in the AMD64 ABI (http://x86-64.org/documentation/abi.pdf) static const Int dwarf_reg_guest_offsets[FJALAR_NUM_DWARF_REGS] = { @@ -141,6 +144,7 @@ static const Int dwarf_reg_guest_offsets[FJALAR_NUM_DWARF_REGS] = { offsetof(VexGuestArchState, guest_R15), }; #else +#define FJALAR_NUM_DWARF_REGS 9 static const Int dwarf_reg_guest_offsets[FJALAR_NUM_DWARF_REGS] = { offsetof(VexGuestArchState, guest_EAX), offsetof(VexGuestArchState, guest_ECX), diff --git a/valgrind/fjalar/fjalar_traversal.c b/valgrind/fjalar/fjalar_traversal.c index ab7b5899..11faceb6 100644 --- a/valgrind/fjalar/fjalar_traversal.c +++ b/valgrind/fjalar/fjalar_traversal.c @@ -1025,7 +1025,7 @@ void visitVariableGroup(VariableOrigin varOrigin, // DWARF supplied constant var_loc = dloc->atom_offset; - } else if(op == DW_OP_plus_uconst) { + } 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; diff --git a/valgrind/fjalar/generate_fjalar_entries.c b/valgrind/fjalar/generate_fjalar_entries.c index 75aa5b39..bf0c952f 100644 --- a/valgrind/fjalar/generate_fjalar_entries.c +++ b/valgrind/fjalar/generate_fjalar_entries.c @@ -2634,10 +2634,7 @@ static void extractOneFormalParameterVar(FunctionEntry* f, varPtr->location_expression_size = paramPtr->dwarf_stack_size; } - if (paramPtr->location_has_unsupported_op) { - // varPtr->validLoc remains 0, so Fjalar omits the formal parameter. - FJALAR_DPRINTF(" location contains an unsupported operation\n"); - } else if (paramPtr->location_type == LT_FP_OFFSET) { + if (paramPtr->location_type == LT_FP_OFFSET) { varPtr->validLoc = paramPtr->valid_loc; varPtr->locationType = FP_OFFSET_LOCATION; varPtr->byteOffset = paramPtr->location; diff --git a/valgrind/fjalar/typedata.c b/valgrind/fjalar/typedata.c index d5fade8f..b96e81ee 100644 --- a/valgrind/fjalar/typedata.c +++ b/valgrind/fjalar/typedata.c @@ -1234,27 +1234,6 @@ char harvest_formal_param_location_offset(dwarf_entry* e, long value) return 0; } -int formal_param_location_stack_size(dwarf_entry* e) -{ - if ((e == 0) || (e->entry_ptr == 0) || !tag_is_formal_parameter(e->tag_name)) - return -1; - return ((formal_parameter*)e->entry_ptr)->dwarf_stack_size; -} - -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; -} - char harvest_data_member_location(dwarf_entry* e, unsigned long value) { unsigned long tag; @@ -1935,7 +1914,6 @@ 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->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 3314ed00..c49e9f3a 100644 --- a/valgrind/fjalar/typedata.h +++ b/valgrind/fjalar/typedata.h @@ -347,11 +347,6 @@ typedef struct // way to get the parameter location unsigned int valid_loc; - // True if the location expression contains an operation that - // dwarf_stack does not record, such as DW_OP_piece or - // DW_OP_stack_value. Fjalar cannot read such a location. - char location_has_unsupported_op; - unsigned long abstract_origin_ID; // See comment in the function struct definition // for the uses of this. @@ -573,10 +568,6 @@ 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); -// Returns the size of the dwarf_stack of e, or -1 if e is not a -// formal parameter. -int formal_param_location_stack_size(dwarf_entry* e); -char harvest_formal_param_location_unsupported(dwarf_entry* e); 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);