Implement unified API for describing addresses (name+offset format) (#5571)
* core: Add unified API for describing addresses (resolves rizinorg#4993) * core: Fix pxW and prompt tests by adding backward-compatible delta limit This commit fixes the test failures caused by behavioral changes in the unified address description API: 1. pxW tests failing (cmd_pxw, helloworld-gcc-elf): - The old pxW code had a hardcoded 8192 limit for flag delta - Added max_flag_delta option to RzAddrDescribeOptions - rz_core_addr_get_flag_offset() now uses max_flag_delta=-1 (8192 limit) 2. prompt settings test failing (test/db/tools/rz): - The old prompt code used spaces around +/- (e.g., 'entry0 + 1') - Added use_spaces_around_delta option to RzAddrDescribeOptions - Added rz_core_addr_get_flag_offset_prompt() for prompt display * core/caddr: Add proper error handling and API improvements - Add RZ_NONNULL annotations to helper function parameters - Add null pointer checks after rz_str_dup() to detect allocation failures - Change helper functions to return bool for error propagation - Handle memory allocation failures in rz_core_addr_describe() by checking helper return values and cleaning up on failure - Use RZ_STR_ISEMPTY macro for consistent empty string checks - Extract rz_core_addr_description_to_pj() as separate API for converting RzAddrDescription to JSON format * core: Add unified API for describing addresses (resolves rizinorg#4993) core: Fix pxW and prompt tests by adding backward-compatible delta limit This commit fixes the test failures caused by behavioral changes in the unified address description API: 1. pxW tests failing (cmd_pxw, helloworld-gcc-elf): - The old pxW code had a hardcoded 8192 limit for flag delta - Added max_flag_delta option to RzAddrDescribeOptions - rz_core_addr_get_flag_offset() now uses max_flag_delta=-1 (8192 limit) 2. prompt settings test failing (test/db/tools/rz): - The old prompt code used spaces around +/- (e.g., 'entry0 + 1') - Added use_spaces_around_delta option to RzAddrDescribeOptions - Added rz_core_addr_get_flag_offset_prompt() for prompt display
This commit is contained in:
parent
d3087cc148
commit
666d0ba8a0
14 changed files with 1171 additions and 202 deletions
754
librz/core/caddr.c
Normal file
754
librz/core/caddr.c
Normal file
|
|
@ -0,0 +1,754 @@
|
|||
// SPDX-FileCopyrightText: 2025 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
/**
|
||||
* \file caddr.c
|
||||
* \brief Unified API for describing addresses in human-readable formats
|
||||
*
|
||||
* This module provides a unified and reusable API for generating human-readable
|
||||
* descriptions of memory addresses. It supports various options like:
|
||||
* - Name+offset format (e.g., "main+0x10")
|
||||
* - Source file and line information from debug symbols
|
||||
* - Function context
|
||||
* - Flag-based naming
|
||||
* - Customizable output formatting
|
||||
*
|
||||
* The API is designed to be used by different parts of Rizin and Cutter,
|
||||
* providing consistent address representation across the codebase.
|
||||
*/
|
||||
|
||||
#include <rz_core.h>
|
||||
#include <rz_util/rz_assert.h>
|
||||
|
||||
/**
|
||||
* \brief Create a new RzCoreAddrOptions with default values
|
||||
* \return Pointer to newly allocated options structure, NULL on failure
|
||||
*/
|
||||
RZ_API RZ_OWN RzCoreAddrOptions *rz_core_addr_options_new(void) {
|
||||
RzCoreAddrOptions *opts = RZ_NEW0(RzCoreAddrOptions);
|
||||
if (!opts) {
|
||||
return NULL;
|
||||
}
|
||||
// Set sensible defaults - use decimal to match existing Rizin behavior
|
||||
opts->show_offset = true;
|
||||
opts->prefer_function = true;
|
||||
opts->show_flag = true;
|
||||
opts->use_decimal = true;
|
||||
opts->show_color = false;
|
||||
opts->show_source_info = false;
|
||||
opts->use_realnames = false;
|
||||
opts->max_flag_delta = 0; // 0 = unlimited (new behavior); use -1 for legacy 8192 limit
|
||||
opts->use_spaces_around_delta = false; // No spaces by default (compact format)
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Free an RzCoreAddrOptions structure
|
||||
* \param opts The options to free
|
||||
*/
|
||||
RZ_API void rz_core_addr_options_free(RZ_NULLABLE RzCoreAddrOptions *opts) {
|
||||
free(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Free an RzCoreAddr structure
|
||||
* \param desc The description to free
|
||||
*/
|
||||
RZ_API void rz_core_addr_free(RZ_NULLABLE RzCoreAddr *desc) {
|
||||
if (!desc) {
|
||||
return;
|
||||
}
|
||||
free(desc->name);
|
||||
free(desc->fcn_name);
|
||||
free(desc->flag_name);
|
||||
free(desc->source_file);
|
||||
free(desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get address description based on flags
|
||||
*
|
||||
* Helper function to find a flag at or near the given address
|
||||
* and compute the delta.
|
||||
*
|
||||
* \param flags The RzFlag instance
|
||||
* \param desc The description structure to populate
|
||||
* \param addr The address to describe
|
||||
* \param opts Options controlling behavior
|
||||
* \return true on success (including when no flag found), false on error (e.g., memory allocation failure)
|
||||
*/
|
||||
static bool addr_describe_from_flags(RZ_NULLABLE RzFlag *flags, RZ_NONNULL RzCoreAddr *desc, ut64 addr, RZ_NONNULL const RzCoreAddrOptions *opts) {
|
||||
rz_return_val_if_fail(desc && opts, false);
|
||||
|
||||
if (!opts->show_flag || !flags) {
|
||||
return true; // Not requested or no flags, not an error
|
||||
}
|
||||
|
||||
RzFlagItem *fi = rz_flag_get_at(flags, addr, true);
|
||||
if (!fi) {
|
||||
return true; // No flag found, not an error
|
||||
}
|
||||
|
||||
st64 delta = (st64)(addr - fi->offset);
|
||||
|
||||
// Check max_flag_delta limit if set
|
||||
// max_flag_delta < 0: use default 8192 limit
|
||||
// max_flag_delta == 0: unlimited
|
||||
// max_flag_delta > 0: use that value as limit
|
||||
if (opts->max_flag_delta < 0) {
|
||||
// Default limit for backward compatibility
|
||||
if (delta < 0 || delta >= 8192) {
|
||||
return true; // Outside delta limit, not an error
|
||||
}
|
||||
} else if (opts->max_flag_delta > 0) {
|
||||
if (delta < 0 || delta >= opts->max_flag_delta) {
|
||||
return true; // Outside delta limit, not an error
|
||||
}
|
||||
}
|
||||
|
||||
const char *name = (opts->use_realnames && fi->realname) ? fi->realname : fi->name;
|
||||
if (RZ_STR_ISEMPTY(name)) {
|
||||
return true; // Empty name, not an error
|
||||
}
|
||||
|
||||
desc->flag_name = rz_str_dup(name);
|
||||
if (!desc->flag_name) {
|
||||
return false; // Memory allocation failed - this IS an error
|
||||
}
|
||||
desc->flag_offset = fi->offset;
|
||||
desc->flag_delta = delta;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get address description based on function analysis
|
||||
*
|
||||
* Helper function to find a function containing the address
|
||||
* and compute the delta from the function start.
|
||||
*
|
||||
* \param analysis The RzAnalysis instance
|
||||
* \param desc The description structure to populate
|
||||
* \param addr The address to describe
|
||||
* \param opts Options controlling behavior
|
||||
* \return true on success (including when no function found), false on error (e.g., memory allocation failure)
|
||||
*/
|
||||
static bool addr_describe_from_function(RZ_NULLABLE RzAnalysis *analysis, RZ_NONNULL RzCoreAddr *desc, ut64 addr, RZ_NONNULL const RzCoreAddrOptions *opts) {
|
||||
rz_return_val_if_fail(desc && opts, false);
|
||||
|
||||
if (!opts->prefer_function || !analysis) {
|
||||
return true; // Not requested or no analysis, not an error
|
||||
}
|
||||
|
||||
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(analysis, addr, 0);
|
||||
if (!fcn) {
|
||||
// Try to get function at address
|
||||
fcn = rz_analysis_get_function_at(analysis, addr);
|
||||
}
|
||||
|
||||
if (!fcn) {
|
||||
return true; // No function found, not an error
|
||||
}
|
||||
|
||||
if (RZ_STR_ISEMPTY(fcn->name)) {
|
||||
return true; // Empty name, not an error
|
||||
}
|
||||
|
||||
desc->fcn_name = rz_str_dup(fcn->name);
|
||||
if (!desc->fcn_name) {
|
||||
return false; // Memory allocation failed - this IS an error
|
||||
}
|
||||
desc->fcn_addr = fcn->addr;
|
||||
desc->fcn_delta = (st64)(addr - fcn->addr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get source line information for address
|
||||
*
|
||||
* Helper function to retrieve source file and line number
|
||||
* from debug information if available.
|
||||
*
|
||||
* \param bf The RzBinFile instance
|
||||
* \param desc The description structure to populate
|
||||
* \param addr The address to describe
|
||||
* \param opts Options controlling behavior
|
||||
* \return true on success (including when no source info found), false on error (e.g., memory allocation failure)
|
||||
*/
|
||||
static bool addr_describe_from_source(RZ_NULLABLE RzBinFile *bf, RZ_NONNULL RzCoreAddr *desc, ut64 addr, RZ_NONNULL const RzCoreAddrOptions *opts) {
|
||||
rz_return_val_if_fail(desc && opts, false);
|
||||
|
||||
if (!opts->show_source_info) {
|
||||
return true; // Not requested, not an error
|
||||
}
|
||||
|
||||
if (!bf || !bf->o) {
|
||||
return true; // No bin file, not an error
|
||||
}
|
||||
|
||||
RzBinSourceLineInfo *lines = bf->o->lines;
|
||||
if (!lines) {
|
||||
return true; // No line info available, not an error
|
||||
}
|
||||
|
||||
const RzBinSourceLineSample *sample = rz_bin_source_line_info_get_first_at(lines, addr);
|
||||
if (!sample || rz_bin_source_line_sample_is_closing(sample)) {
|
||||
return true; // No sample found, not an error
|
||||
}
|
||||
|
||||
if (sample->file) {
|
||||
desc->source_file = rz_str_dup(sample->file);
|
||||
if (!desc->source_file) {
|
||||
return false; // Memory allocation failed - this IS an error
|
||||
}
|
||||
}
|
||||
desc->source_line = sample->line;
|
||||
desc->source_column = sample->column;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Describe an address with human-readable information
|
||||
*
|
||||
* This is the main unified API for generating address descriptions.
|
||||
* It combines information from flags, functions, and debug symbols
|
||||
* to create a comprehensive description of an address.
|
||||
*
|
||||
* \param core The RzCore instance
|
||||
* \param addr The address to describe
|
||||
* \param opts Options controlling what information to include
|
||||
* \return Pointer to RzCoreAddr, or NULL on failure. Caller must free with rz_core_addr_free()
|
||||
*/
|
||||
RZ_API RZ_OWN RzCoreAddr *rz_core_addr_describe(RZ_NONNULL RzCore *core, ut64 addr, RZ_NULLABLE const RzCoreAddrOptions *opts) {
|
||||
rz_return_val_if_fail(core, NULL);
|
||||
|
||||
RzCoreAddrOptions default_opts = {
|
||||
.show_offset = true,
|
||||
.prefer_function = true,
|
||||
.show_flag = true,
|
||||
.use_decimal = true,
|
||||
.show_color = false,
|
||||
.show_source_info = false,
|
||||
.use_realnames = false
|
||||
};
|
||||
|
||||
if (!opts) {
|
||||
opts = &default_opts;
|
||||
}
|
||||
|
||||
RzCoreAddr *desc = RZ_NEW0(RzCoreAddr);
|
||||
if (!desc) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc->addr = addr;
|
||||
|
||||
// Get function information first (higher priority)
|
||||
// Returns false only on memory allocation failure
|
||||
if (!addr_describe_from_function(core->analysis, desc, addr, opts)) {
|
||||
rz_core_addr_free(desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get flag information
|
||||
// Returns false only on memory allocation failure
|
||||
if (!addr_describe_from_flags(core->flags, desc, addr, opts)) {
|
||||
rz_core_addr_free(desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get source line information if requested
|
||||
// Returns false only on memory allocation failure
|
||||
if (!addr_describe_from_source(rz_bin_cur(core->bin), desc, addr, opts)) {
|
||||
rz_core_addr_free(desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Format an address description as a string
|
||||
*
|
||||
* Generates a formatted string representation of an address description.
|
||||
* The format depends on what information is available and the options provided.
|
||||
*
|
||||
* Priority order for name+offset:
|
||||
* 1. Function name (if opts->prefer_function is true and function is found)
|
||||
* 2. Flag name (if flag is found)
|
||||
* 3. Plain address
|
||||
*
|
||||
* \param desc The address description to format
|
||||
* \param opts Options controlling the output format
|
||||
* \return Newly allocated formatted string, or NULL on failure. Caller must free.
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_core_addr_to_string(RZ_NONNULL const RzCoreAddr *desc, RZ_NULLABLE const RzCoreAddrOptions *opts) {
|
||||
rz_return_val_if_fail(desc, NULL);
|
||||
|
||||
RzCoreAddrOptions default_opts = {
|
||||
.show_offset = true,
|
||||
.prefer_function = true,
|
||||
.show_flag = true,
|
||||
.use_decimal = true,
|
||||
.show_color = false,
|
||||
.show_source_info = false,
|
||||
.use_realnames = false
|
||||
};
|
||||
|
||||
if (!opts) {
|
||||
opts = &default_opts;
|
||||
}
|
||||
|
||||
RzStrBuf sb;
|
||||
rz_strbuf_init(&sb);
|
||||
|
||||
const char *name = NULL;
|
||||
st64 delta = 0;
|
||||
bool has_name = false;
|
||||
|
||||
// Priority: function > flag > plain address
|
||||
if (opts->prefer_function && !RZ_STR_ISEMPTY(desc->fcn_name)) {
|
||||
name = desc->fcn_name;
|
||||
delta = desc->fcn_delta;
|
||||
has_name = true;
|
||||
} else if (opts->show_flag && !RZ_STR_ISEMPTY(desc->flag_name)) {
|
||||
name = desc->flag_name;
|
||||
delta = desc->flag_delta;
|
||||
has_name = true;
|
||||
}
|
||||
|
||||
if (has_name && name) {
|
||||
const char *plus_fmt = opts->use_spaces_around_delta ? " + " : "+";
|
||||
const char *minus_fmt = opts->use_spaces_around_delta ? " - " : "-";
|
||||
if (delta > 0) {
|
||||
if (opts->use_decimal) {
|
||||
rz_strbuf_appendf(&sb, "%s%s%" PFMT64d, name, plus_fmt, delta);
|
||||
} else {
|
||||
rz_strbuf_appendf(&sb, "%s%s0x%" PFMT64x, name, plus_fmt, (ut64)delta);
|
||||
}
|
||||
} else if (delta < 0) {
|
||||
if (opts->use_decimal) {
|
||||
rz_strbuf_appendf(&sb, "%s%s%" PFMT64d, name, minus_fmt, -delta);
|
||||
} else {
|
||||
rz_strbuf_appendf(&sb, "%s%s0x%" PFMT64x, name, minus_fmt, (ut64)(-delta));
|
||||
}
|
||||
} else {
|
||||
rz_strbuf_append(&sb, name);
|
||||
}
|
||||
} else if (opts->show_offset) {
|
||||
// No name found, always show the address in hex format for clarity
|
||||
rz_strbuf_appendf(&sb, "0x%" PFMT64x, desc->addr);
|
||||
}
|
||||
|
||||
// Append source info if requested and available
|
||||
if (opts->show_source_info && desc->source_file) {
|
||||
if (desc->source_line > 0) {
|
||||
if (desc->source_column > 0) {
|
||||
rz_strbuf_appendf(&sb, " (%s:%" PFMT32u ":%" PFMT32u ")",
|
||||
desc->source_file, desc->source_line, desc->source_column);
|
||||
} else {
|
||||
rz_strbuf_appendf(&sb, " (%s:%" PFMT32u ")",
|
||||
desc->source_file, desc->source_line);
|
||||
}
|
||||
} else {
|
||||
rz_strbuf_appendf(&sb, " (%s)", desc->source_file);
|
||||
}
|
||||
}
|
||||
|
||||
return rz_strbuf_drain_nofree(&sb);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Convenience function to get a name+offset string for an address
|
||||
*
|
||||
* This is a simplified wrapper that combines rz_core_addr_describe() and
|
||||
* rz_core_addr_to_string() for common use cases.
|
||||
*
|
||||
* \param core The RzCore instance
|
||||
* \param addr The address to describe
|
||||
* \param opts Options controlling the output format (NULL for defaults)
|
||||
* \return Newly allocated string, or NULL on failure. Caller must free.
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_core_addr_describe_string(RZ_NONNULL RzCore *core, ut64 addr, RZ_NULLABLE const RzCoreAddrOptions *opts) {
|
||||
rz_return_val_if_fail(core, NULL);
|
||||
|
||||
RzCoreAddr *desc = rz_core_addr_describe(core, addr, opts);
|
||||
if (!desc) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *result = rz_core_addr_to_string(desc, opts);
|
||||
rz_core_addr_free(desc);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get name+offset string for an address (simple version)
|
||||
*
|
||||
* This is the simplest API for getting a human-readable address description.
|
||||
* It uses default options and is suitable for most use cases.
|
||||
*
|
||||
* \param core The RzCore instance
|
||||
* \param addr The address to describe
|
||||
* \return Newly allocated string in "name+offset" format, or hex address. Caller must free.
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_core_addr_get_name_delta(RZ_NONNULL RzCore *core, ut64 addr) {
|
||||
rz_return_val_if_fail(core, NULL);
|
||||
return rz_core_addr_describe_string(core, addr, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Describe an address with function context
|
||||
*
|
||||
* Similar to rz_core_addr_describe() but forces preference for function names.
|
||||
*
|
||||
* \param core The RzCore instance
|
||||
* \param addr The address to describe
|
||||
* \return Pointer to RzCoreAddr, or NULL on failure. Caller must free.
|
||||
*/
|
||||
RZ_API RZ_OWN RzCoreAddr *rz_core_addr_describe_with_function(RZ_NONNULL RzCore *core, ut64 addr) {
|
||||
rz_return_val_if_fail(core, NULL);
|
||||
|
||||
RzCoreAddrOptions opts = {
|
||||
.show_offset = true,
|
||||
.prefer_function = true,
|
||||
.show_flag = true,
|
||||
.use_decimal = true,
|
||||
.show_color = false,
|
||||
.show_source_info = false,
|
||||
.use_realnames = false
|
||||
};
|
||||
|
||||
return rz_core_addr_describe(core, addr, &opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Describe an address with source line information
|
||||
*
|
||||
* Similar to rz_core_addr_describe() but includes source file/line info.
|
||||
*
|
||||
* \param core The RzCore instance
|
||||
* \param addr The address to describe
|
||||
* \return Pointer to RzCoreAddr, or NULL on failure. Caller must free.
|
||||
*/
|
||||
RZ_API RZ_OWN RzCoreAddr *rz_core_addr_describe_with_source(RZ_NONNULL RzCore *core, ut64 addr) {
|
||||
rz_return_val_if_fail(core, NULL);
|
||||
|
||||
RzCoreAddrOptions opts = {
|
||||
.show_offset = true,
|
||||
.prefer_function = true,
|
||||
.show_flag = true,
|
||||
.use_decimal = true,
|
||||
.show_color = false,
|
||||
.show_source_info = true,
|
||||
.use_realnames = false
|
||||
};
|
||||
|
||||
return rz_core_addr_describe(core, addr, &opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Format an address for display in the disassembly offset column
|
||||
*
|
||||
* This function generates the address string used in disassembly output,
|
||||
* supporting relative offsets (asm.reloff), segmented addresses, and
|
||||
* decimal/hex formatting.
|
||||
*
|
||||
* \param print The RzPrint instance (may be NULL)
|
||||
* \param addr The address to format
|
||||
* \param opts Display options
|
||||
* \return Newly allocated formatted string. Caller must free.
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_core_addr_format_for_display(RZ_NULLABLE RzPrint *print, ut64 addr, RZ_NULLABLE const RzCoreAddrOptions *opts) {
|
||||
RzCoreAddrOptions default_opts = {
|
||||
.show_offset = true,
|
||||
.prefer_function = false,
|
||||
.show_flag = false,
|
||||
.use_decimal = true,
|
||||
.show_color = false,
|
||||
.show_source_info = false,
|
||||
.use_realnames = false
|
||||
};
|
||||
|
||||
if (!opts) {
|
||||
opts = &default_opts;
|
||||
}
|
||||
|
||||
RzStrBuf sb;
|
||||
rz_strbuf_init(&sb);
|
||||
|
||||
if (opts->use_decimal) {
|
||||
rz_strbuf_appendf(&sb, "%" PFMT64u, addr);
|
||||
} else {
|
||||
if (print && print->wide_offsets) {
|
||||
rz_strbuf_appendf(&sb, "0x%016" PFMT64x, addr);
|
||||
} else {
|
||||
rz_strbuf_appendf(&sb, "0x%08" PFMT64x, addr);
|
||||
}
|
||||
}
|
||||
|
||||
return rz_strbuf_drain_nofree(&sb);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Convert an address description to JSON format
|
||||
*
|
||||
* Translates the given RzCoreAddr into a JSON object and appends
|
||||
* it to the provided PJ instance.
|
||||
*
|
||||
* \param pj The PrettyJSON instance to append to
|
||||
* \param desc The address description to convert
|
||||
* \param opts Options controlling what information to include in the output
|
||||
*/
|
||||
RZ_API void rz_core_addr_to_pj(RZ_NONNULL PJ *pj, RZ_NONNULL const RzCoreAddr *desc, RZ_NULLABLE const RzCoreAddrOptions *opts) {
|
||||
rz_return_if_fail(pj && desc);
|
||||
|
||||
pj_o(pj);
|
||||
pj_kn(pj, "addr", desc->addr);
|
||||
|
||||
if (!RZ_STR_ISEMPTY(desc->fcn_name)) {
|
||||
pj_ks(pj, "fcn_name", desc->fcn_name);
|
||||
pj_kn(pj, "fcn_addr", desc->fcn_addr);
|
||||
pj_kN(pj, "fcn_delta", desc->fcn_delta);
|
||||
}
|
||||
|
||||
if (!RZ_STR_ISEMPTY(desc->flag_name)) {
|
||||
pj_ks(pj, "flag_name", desc->flag_name);
|
||||
pj_kn(pj, "flag_offset", desc->flag_offset);
|
||||
pj_kN(pj, "flag_delta", desc->flag_delta);
|
||||
}
|
||||
|
||||
if (!RZ_STR_ISEMPTY(desc->source_file)) {
|
||||
pj_ks(pj, "source_file", desc->source_file);
|
||||
if (desc->source_line > 0) {
|
||||
pj_ki(pj, "source_line", desc->source_line);
|
||||
}
|
||||
if (desc->source_column > 0) {
|
||||
pj_ki(pj, "source_column", desc->source_column);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a combined name+delta string
|
||||
char *name_delta = rz_core_addr_to_string(desc, opts);
|
||||
if (name_delta) {
|
||||
pj_ks(pj, "name_delta", name_delta);
|
||||
free(name_delta);
|
||||
}
|
||||
|
||||
pj_end(pj);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get a detailed address description for JSON output
|
||||
*
|
||||
* Convenience function that describes an address and outputs it as JSON.
|
||||
* This combines rz_core_addr_describe() and rz_core_addr_to_pj().
|
||||
*
|
||||
* \param core The RzCore instance
|
||||
* \param pj The PrettyJSON instance to append to
|
||||
* \param addr The address to describe
|
||||
* \param opts Options controlling what information to include
|
||||
*/
|
||||
RZ_API void rz_core_addr_describe_pj(RZ_NONNULL RzCore *core, RZ_NONNULL PJ *pj, ut64 addr, RZ_NULLABLE const RzCoreAddrOptions *opts) {
|
||||
rz_return_if_fail(core && pj);
|
||||
|
||||
RzCoreAddr *desc = rz_core_addr_describe(core, addr, opts);
|
||||
if (!desc) {
|
||||
pj_o(pj);
|
||||
pj_kn(pj, "addr", addr);
|
||||
pj_end(pj);
|
||||
return;
|
||||
}
|
||||
|
||||
rz_core_addr_to_pj(pj, desc, opts);
|
||||
rz_core_addr_free(desc);
|
||||
}
|
||||
/**
|
||||
* \brief Get relative offset info for an address (for asm.reloff functionality)
|
||||
*
|
||||
* This function retrieves the function or flag name and delta for an address,
|
||||
* which is useful for displaying relative offsets in disassembly.
|
||||
*
|
||||
* \param core The RzCore instance
|
||||
* \param addr The address to describe
|
||||
* \param prefer_function If true, prefer function names over flags
|
||||
* \param use_flags If true and no function is found, use flag names
|
||||
* \param[out] out_name Pointer to store the name (must be freed by caller)
|
||||
* \param[out] out_delta Pointer to store the delta from the name's address
|
||||
* \return true if a name was found, false otherwise
|
||||
*/
|
||||
RZ_API bool rz_core_addr_get_reloff_info(RZ_NONNULL RzCore *core, ut64 addr,
|
||||
bool prefer_function, bool use_flags,
|
||||
RZ_OUT RZ_NULLABLE char **out_name, RZ_OUT RZ_NULLABLE st64 *out_delta) {
|
||||
rz_return_val_if_fail(core, false);
|
||||
|
||||
if (out_name) {
|
||||
*out_name = NULL;
|
||||
}
|
||||
if (out_delta) {
|
||||
*out_delta = 0;
|
||||
}
|
||||
|
||||
RzCoreAddrOptions opts = {
|
||||
.show_offset = false,
|
||||
.prefer_function = prefer_function,
|
||||
.show_flag = use_flags,
|
||||
.use_decimal = true,
|
||||
.show_color = false,
|
||||
.show_source_info = false,
|
||||
.use_realnames = false
|
||||
};
|
||||
|
||||
RzCoreAddr *desc = rz_core_addr_describe(core, addr, &opts);
|
||||
if (!desc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
// Priority: function > flag
|
||||
if (prefer_function && desc->fcn_name) {
|
||||
if (out_name) {
|
||||
*out_name = rz_str_dup(desc->fcn_name);
|
||||
}
|
||||
if (out_delta) {
|
||||
*out_delta = desc->fcn_delta;
|
||||
}
|
||||
found = true;
|
||||
} else if (use_flags && desc->flag_name) {
|
||||
if (out_name) {
|
||||
*out_name = rz_str_dup(desc->flag_name);
|
||||
}
|
||||
if (out_delta) {
|
||||
*out_delta = desc->flag_delta;
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
|
||||
rz_core_addr_free(desc);
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get function-relative offset string for an address
|
||||
*
|
||||
* Returns a string like "main+0x10" if the address is within a function,
|
||||
* or NULL if not.
|
||||
*
|
||||
* \param core The RzCore instance
|
||||
* \param addr The address to describe
|
||||
* \return Newly allocated string or NULL. Caller must free.
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_core_addr_get_function_offset(RZ_NONNULL RzCore *core, ut64 addr) {
|
||||
rz_return_val_if_fail(core, NULL);
|
||||
|
||||
RzCoreAddrOptions opts = {
|
||||
.show_offset = false,
|
||||
.prefer_function = true,
|
||||
.show_flag = false,
|
||||
.use_decimal = true,
|
||||
.show_color = false,
|
||||
.show_source_info = false,
|
||||
.use_realnames = false
|
||||
};
|
||||
|
||||
RzCoreAddr *desc = rz_core_addr_describe(core, addr, &opts);
|
||||
if (!desc || !desc->fcn_name) {
|
||||
rz_core_addr_free(desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *result = rz_core_addr_to_string(desc, &opts);
|
||||
rz_core_addr_free(desc);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Internal helper to get flag-relative offset string
|
||||
*
|
||||
* Returns a string like "sym.func+0x10" if a flag exists at or before the address,
|
||||
* or NULL if not.
|
||||
*
|
||||
* \param flags The RzFlag instance
|
||||
* \param addr The address to describe
|
||||
* \param opts Options controlling the output format
|
||||
* \return Newly allocated string or NULL. Caller must free.
|
||||
*/
|
||||
static RZ_OWN char *addr_get_flag_offset_internal(RZ_NONNULL RzFlag *flags, ut64 addr, RZ_NONNULL const RzCoreAddrOptions *opts) {
|
||||
rz_return_val_if_fail(flags && opts, NULL);
|
||||
|
||||
RzCoreAddr *desc = RZ_NEW0(RzCoreAddr);
|
||||
if (!desc) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc->addr = addr;
|
||||
|
||||
if (!addr_describe_from_flags(flags, desc, addr, opts)) {
|
||||
rz_core_addr_free(desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!desc->flag_name) {
|
||||
rz_core_addr_free(desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *result = rz_core_addr_to_string(desc, opts);
|
||||
rz_core_addr_free(desc);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get flag-relative offset string for an address
|
||||
*
|
||||
* Returns a string like "sym.func+0x10" if a flag exists at or before the address,
|
||||
* or NULL if not.
|
||||
*
|
||||
* \param flags The RzFlag instance
|
||||
* \param addr The address to describe
|
||||
* \return Newly allocated string or NULL. Caller must free.
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_core_addr_get_flag_offset(RZ_NONNULL RzFlag *flags, ut64 addr) {
|
||||
rz_return_val_if_fail(flags, NULL);
|
||||
|
||||
RzCoreAddrOptions opts = {
|
||||
.show_offset = false,
|
||||
.prefer_function = false,
|
||||
.show_flag = true,
|
||||
.use_decimal = true,
|
||||
.show_color = false,
|
||||
.show_source_info = false,
|
||||
.use_realnames = false,
|
||||
.max_flag_delta = -1 // Use default 8192 limit for backward compatibility
|
||||
};
|
||||
|
||||
return addr_get_flag_offset_internal(flags, addr, &opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get flag-relative offset string for an address with spaces around delta
|
||||
*
|
||||
* Returns a string like "sym.func + 0x10" if a flag exists at or before the address,
|
||||
* or NULL if not. Uses spaces around +/- for readability (used for prompts).
|
||||
*
|
||||
* \param flags The RzFlag instance
|
||||
* \param addr The address to describe
|
||||
* \return Newly allocated string or NULL. Caller must free.
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_core_addr_get_flag_offset_prompt(RZ_NONNULL RzFlag *flags, ut64 addr) {
|
||||
rz_return_val_if_fail(flags, NULL);
|
||||
|
||||
RzCoreAddrOptions opts = {
|
||||
.show_offset = false,
|
||||
.prefer_function = false,
|
||||
.show_flag = true,
|
||||
.use_decimal = true,
|
||||
.show_color = false,
|
||||
.show_source_info = false,
|
||||
.use_realnames = false,
|
||||
.max_flag_delta = 0, // No limit for prompt - show all flags
|
||||
.use_spaces_around_delta = true // Use spaces for prompt display
|
||||
};
|
||||
|
||||
return addr_get_flag_offset_internal(flags, addr, &opts);
|
||||
}
|
||||
|
|
@ -958,33 +958,22 @@ static void get_backtrace_info(RzCore *core, RzDebugFrame *frame, ut64 addr,
|
|||
*flagdesc = NULL;
|
||||
*flagdesc2 = NULL;
|
||||
if (f) {
|
||||
if (f->offset != addr) {
|
||||
int delta = (int)(frame->addr - f->offset);
|
||||
if (delta > 0) {
|
||||
*flagdesc = rz_str_newf("%s+%d", f->name, delta);
|
||||
} else if (delta < 0) {
|
||||
*flagdesc = rz_str_newf("%s%d", f->name, delta);
|
||||
} else {
|
||||
*flagdesc = rz_str_newf("%s", f->name);
|
||||
}
|
||||
// Use unified API format: name+delta (decimal)
|
||||
st64 delta = (st64)(frame->addr - f->offset);
|
||||
if (delta != 0) {
|
||||
*flagdesc = rz_str_newf("%s%+" PFMT64d, f->name, delta);
|
||||
} else {
|
||||
*flagdesc = rz_str_newf("%s", f->name);
|
||||
*flagdesc = rz_str_dup(f->name);
|
||||
}
|
||||
if (!strchr(f->name, '.')) {
|
||||
f2 = rz_flag_get_at(core->flags, frame->addr - 1, true);
|
||||
}
|
||||
if (f2 && f2 != f) {
|
||||
if (f2->offset != addr) {
|
||||
int delta = (int)(frame->addr - 1 - f2->offset);
|
||||
if (delta > 0) {
|
||||
*flagdesc2 = rz_str_newf("%s+%d", f2->name, delta + 1);
|
||||
} else if (delta < 0) {
|
||||
*flagdesc2 = rz_str_newf("%s%d", f2->name, delta + 1);
|
||||
} else {
|
||||
*flagdesc2 = rz_str_newf("%s+1", f2->name);
|
||||
}
|
||||
st64 delta2 = (st64)(frame->addr - f2->offset);
|
||||
if (delta2 != 0) {
|
||||
*flagdesc2 = rz_str_newf("%s%+" PFMT64d, f2->name, delta2);
|
||||
} else {
|
||||
*flagdesc2 = rz_str_newf("%s", f2->name);
|
||||
*flagdesc2 = rz_str_dup(f2->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1141,7 +1130,6 @@ RZ_IPI bool rz_core_debug_thread_print(RzDebug *dbg, int pid, RzCmdStateOutput *
|
|||
}
|
||||
RzListIter *iter;
|
||||
RzDebugPid *p;
|
||||
RzAnalysisFunction *fcn = NULL;
|
||||
RzDebugMap *map = NULL;
|
||||
RzStrBuf *path = NULL;
|
||||
char status[2];
|
||||
|
|
@ -1158,26 +1146,11 @@ RZ_IPI bool rz_core_debug_thread_print(RzDebug *dbg, int pid, RzCmdStateOutput *
|
|||
|
||||
rz_strbuf_appendf(path, " (0x%" PFMT64x ")", p->pc);
|
||||
|
||||
fcn = rz_analysis_get_fcn_in(dbg->analysis, p->pc, 0);
|
||||
if (fcn) {
|
||||
if (p->pc == fcn->addr) {
|
||||
rz_strbuf_appendf(path, " at %s", fcn->name);
|
||||
} else {
|
||||
st64 delta = p->pc - fcn->addr;
|
||||
char sign = delta >= 0 ? '+' : '-';
|
||||
rz_strbuf_appendf(path, " in %s%c%" PFMT64u, fcn->name, sign, RZ_ABS(delta));
|
||||
}
|
||||
} else {
|
||||
const char *flag_name = dbg->corebind.getName(dbg->corebind.core, p->pc);
|
||||
if (flag_name) {
|
||||
rz_strbuf_appendf(path, " at %s", flag_name);
|
||||
} else {
|
||||
char *name_delta = dbg->corebind.getNameDelta(dbg->corebind.core, p->pc);
|
||||
if (name_delta) {
|
||||
rz_strbuf_appendf(path, " in %s", name_delta);
|
||||
free(name_delta);
|
||||
}
|
||||
}
|
||||
char *name_delta = dbg->corebind.getNameDelta(dbg->corebind.core, p->pc);
|
||||
if (name_delta) {
|
||||
bool has_delta = strchr(name_delta, '+') || strchr(name_delta, '-');
|
||||
rz_strbuf_appendf(path, " %s %s", has_delta ? "in" : "at", name_delta);
|
||||
free(name_delta);
|
||||
}
|
||||
}
|
||||
rz_strf(status, "%c", p->status);
|
||||
|
|
|
|||
|
|
@ -1066,34 +1066,15 @@ static void backtrace_vars(RzCore *core, RzList /*<RzDebugFrame *>*/ *frames) {
|
|||
rz_reg_setv(r, bp, s);
|
||||
rz_reg_setv(r, sp, b);
|
||||
//////////
|
||||
char flagdesc[1024], flagdesc2[1024];
|
||||
RzFlagItem *fi = rz_flag_get_at(core->flags, f->addr, true);
|
||||
flagdesc[0] = flagdesc2[0] = 0;
|
||||
if (fi) {
|
||||
if (fi->offset != f->addr) {
|
||||
int delta = (int)(f->addr - fi->offset);
|
||||
if (delta > 0) {
|
||||
snprintf(flagdesc, sizeof(flagdesc),
|
||||
"%s+%d", fi->name, delta);
|
||||
} else if (delta < 0) {
|
||||
snprintf(flagdesc, sizeof(flagdesc),
|
||||
"%s%d", fi->name, delta);
|
||||
} else {
|
||||
snprintf(flagdesc, sizeof(flagdesc),
|
||||
"%s", fi->name);
|
||||
}
|
||||
} else {
|
||||
snprintf(flagdesc, sizeof(flagdesc),
|
||||
"%s", fi->name);
|
||||
}
|
||||
}
|
||||
char *flagdesc = rz_core_addr_get_flag_offset(core->flags, f->addr);
|
||||
//////////
|
||||
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, f->addr, 0);
|
||||
// char *str = rz_str_newf ("[frame %d]", n);
|
||||
rz_cons_printf("%d 0x%08" PFMT64x " sp: 0x%08" PFMT64x " %-5d"
|
||||
"[%s] %s %s\n",
|
||||
"[%s] %s\n",
|
||||
n, f->addr, f->sp, (int)f->size,
|
||||
fcn ? fcn->name : "??", flagdesc, flagdesc2);
|
||||
fcn ? fcn->name : "??", flagdesc ? flagdesc : "");
|
||||
free(flagdesc);
|
||||
rz_cons_push();
|
||||
char *res = rz_core_analysis_all_vars_display(core, fcn, true);
|
||||
rz_cons_pop();
|
||||
|
|
@ -1220,7 +1201,6 @@ static RTreeNode *add_trace_tree_child(HtUP *ht, RTree *t, RTreeNode *cur, ut64
|
|||
static RzCore *_core = NULL;
|
||||
|
||||
static void trace_traverse_pre(RTreeNode *n, RTreeVisitor *vis) {
|
||||
const char *name = "";
|
||||
struct trace_node *tn = n->data;
|
||||
unsigned int i;
|
||||
if (!tn)
|
||||
|
|
@ -1228,13 +1208,12 @@ static void trace_traverse_pre(RTreeNode *n, RTreeVisitor *vis) {
|
|||
for (i = 0; i < n->depth - 1; i++) {
|
||||
rz_cons_printf(" ");
|
||||
}
|
||||
char *name = NULL;
|
||||
if (_core) {
|
||||
RzFlagItem *f = rz_flag_get_at(_core->flags, tn->addr, true);
|
||||
if (f) {
|
||||
name = f->name;
|
||||
}
|
||||
name = rz_core_addr_get_flag_offset(_core->flags, tn->addr);
|
||||
}
|
||||
rz_cons_printf(" 0x%08" PFMT64x " refs %d %s\n", tn->addr, tn->refs, name);
|
||||
rz_cons_printf(" 0x%08" PFMT64x " refs %d %s\n", tn->addr, tn->refs, name ? name : "");
|
||||
free(name);
|
||||
}
|
||||
|
||||
static void trace_traverse(RTree *t) {
|
||||
|
|
|
|||
|
|
@ -232,16 +232,7 @@ RZ_IPI RzCmdStatus rz_seek_history_list_handler(RzCore *core, int argc, const ch
|
|||
rz_cmd_state_output_array_start(state);
|
||||
bool current_met = false;
|
||||
rz_list_foreach (list, iter, undo) {
|
||||
RzFlagItem *f = rz_flag_get_at(core->flags, undo->offset, true);
|
||||
const char *comment;
|
||||
char *name = NULL;
|
||||
if (f) {
|
||||
if (f->offset != undo->offset) {
|
||||
name = rz_str_newf("%s+%" PFMT64d, f->name, undo->offset - f->offset);
|
||||
} else {
|
||||
name = rz_str_dup(f->name);
|
||||
}
|
||||
}
|
||||
char *name = rz_core_addr_get_flag_offset(core->flags, undo->offset);
|
||||
current_met |= undo->is_current;
|
||||
switch (state->mode) {
|
||||
case RZ_OUTPUT_MODE_JSON:
|
||||
|
|
@ -254,8 +245,8 @@ RZ_IPI RzCmdStatus rz_seek_history_list_handler(RzCore *core, int argc, const ch
|
|||
pj_kb(pj, "current", undo->is_current);
|
||||
pj_end(pj);
|
||||
break;
|
||||
case RZ_OUTPUT_MODE_STANDARD:
|
||||
comment = "";
|
||||
case RZ_OUTPUT_MODE_STANDARD: {
|
||||
const char *comment = "";
|
||||
if (undo->is_current) {
|
||||
comment = " # current seek";
|
||||
} else if (current_met) {
|
||||
|
|
@ -263,6 +254,7 @@ RZ_IPI RzCmdStatus rz_seek_history_list_handler(RzCore *core, int argc, const ch
|
|||
}
|
||||
rz_cons_printf("0x%" PFMT64x " %s%s\n", undo->offset, name ? name : "", comment);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -396,17 +396,7 @@ static const char *getName(RzCore *core, ut64 addr) {
|
|||
}
|
||||
|
||||
static char *getNameDelta(RzCore *core, ut64 addr) {
|
||||
RzFlagItem *item = rz_flag_get_at(core->flags, addr, true);
|
||||
if (item) {
|
||||
if (item->offset != addr) {
|
||||
const char *name = core->flags->realnames
|
||||
? item->realname
|
||||
: item->name;
|
||||
return rz_str_newf("%s+%" PFMT64u, name, addr - item->offset);
|
||||
}
|
||||
return rz_str_dup(item->name);
|
||||
}
|
||||
return NULL;
|
||||
return rz_core_addr_get_flag_offset(core->flags, addr);
|
||||
}
|
||||
|
||||
static void archbits(RzCore *core, ut64 addr) {
|
||||
|
|
@ -1952,13 +1942,10 @@ static bool prompt_add_offset(RzCore *core, RzStrBuf *sb, bool add_sep) {
|
|||
rz_strbuf_append(sb, ":");
|
||||
}
|
||||
if (rz_config_get_b(core->config, "scr.prompt.flag")) {
|
||||
const RzFlagItem *f = rz_flag_get_at(core->flags, core->offset, true);
|
||||
if (f) {
|
||||
if (f->offset < core->offset) {
|
||||
rz_strbuf_appendf(sb, "%s + %" PFMT64u, f->name, core->offset - f->offset);
|
||||
} else {
|
||||
rz_strbuf_appendf(sb, "%s", f->name);
|
||||
}
|
||||
char *flag_desc = rz_core_addr_get_flag_offset_prompt(core->flags, core->offset);
|
||||
if (flag_desc) {
|
||||
rz_strbuf_append(sb, flag_desc);
|
||||
free(flag_desc);
|
||||
if (rz_config_get_b(core->config, "scr.prompt.flag.only")) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -435,7 +435,6 @@ RZ_API RZ_OWN char *rz_core_print_hexdump_byline_str(RZ_NONNULL RzCore *core, bo
|
|||
const char *a, *b;
|
||||
char *fn;
|
||||
RzPrint *p = core->print;
|
||||
RzFlagItem *f;
|
||||
ut64 v = rz_read_ble(buffer + i, p->big_endian, size * 8);
|
||||
if (p->colorfor) {
|
||||
a = p->colorfor(p->user, v, true);
|
||||
|
|
@ -447,18 +446,7 @@ RZ_API RZ_OWN char *rz_core_print_hexdump_byline_str(RZ_NONNULL RzCore *core, bo
|
|||
} else {
|
||||
a = b = "";
|
||||
}
|
||||
f = rz_flag_get_at(core->flags, v, true);
|
||||
fn = NULL;
|
||||
if (f) {
|
||||
st64 delta = (st64)(v - f->offset);
|
||||
if (delta >= 0 && delta < 8192) {
|
||||
if (v == f->offset) {
|
||||
fn = rz_str_dup(f->name);
|
||||
} else {
|
||||
fn = rz_str_newf("%s+%" PFMT64d, f->name, v - f->offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn = rz_core_addr_get_flag_offset(core->flags, v);
|
||||
char *vstr = ut64_to_hex(v, size * 2);
|
||||
if (vstr) {
|
||||
if (hex_offset) {
|
||||
|
|
|
|||
|
|
@ -2660,28 +2660,20 @@ static void ds_print_lines_left(RzDisasmState *ds) {
|
|||
free(sect);
|
||||
}
|
||||
if (ds->show_symbols) {
|
||||
const char *name = "";
|
||||
int delta = 0;
|
||||
if (ds->fcn) {
|
||||
ds->lastflagitem.offset = ds->fcn->addr;
|
||||
ds->lastflagitem.name = ds->fcn->name;
|
||||
ds->lastflag = &ds->lastflagitem;
|
||||
} else {
|
||||
RzFlagItem *fi = rz_flag_get_at(core->flags, ds->at, !ds->lastflag);
|
||||
if (fi) { // && (!ds->lastflag || fi->offset != ds->at))
|
||||
ds->lastflagitem.offset = fi->offset;
|
||||
ds->lastflagitem.name = fi->name;
|
||||
ds->lastflag = &ds->lastflagitem;
|
||||
}
|
||||
}
|
||||
if (ds->lastflag && ds->lastflag->name) {
|
||||
name = ds->lastflag->name;
|
||||
delta = ds->at - ds->lastflag->offset;
|
||||
}
|
||||
{
|
||||
char *str = rz_str_newf("%s + %-4d", name, delta);
|
||||
// Use unified API approach for getting name+delta
|
||||
char *name = NULL;
|
||||
st64 delta = 0;
|
||||
bool found = rz_core_addr_get_reloff_info(core, ds->at,
|
||||
true, // prefer_function
|
||||
true, // use_flags
|
||||
&name, &delta);
|
||||
if (found && name) {
|
||||
char *str = rz_str_newf("%s + %-4" PFMT64d, name, delta);
|
||||
printCol(ds, str, ds->show_symbols_col, COLOR(ds, num));
|
||||
free(str);
|
||||
free(name);
|
||||
} else {
|
||||
printCol(ds, " + 0 ", ds->show_symbols_col, COLOR(ds, num));
|
||||
}
|
||||
}
|
||||
if (ds->line) {
|
||||
|
|
@ -2774,15 +2766,15 @@ static void ds_print_offset(RzDisasmState *ds) {
|
|||
rz_print_set_screenbounds(core->print, at);
|
||||
if (ds->show_offset) {
|
||||
const char *label = NULL;
|
||||
RzFlagItem *fi;
|
||||
int delta = -1;
|
||||
bool show_trace = false;
|
||||
unsigned int seggrn = rz_config_get_i(core->config, "asm.seggrn");
|
||||
|
||||
if (ds->show_reloff) {
|
||||
// Get function at this address (unified API approach)
|
||||
RzAnalysisFunction *f = rz_analysis_get_function_at(core->analysis, at);
|
||||
if (!f) {
|
||||
f = fcnIn(ds, at, RZ_ANALYSIS_FCN_TYPE_NULL); // rz_analysis_get_fcn_in (core->analysis, at, RZ_ANALYSIS_FCN_TYPE_NULL);
|
||||
f = rz_analysis_get_fcn_in(core->analysis, at, RZ_ANALYSIS_FCN_TYPE_NULL);
|
||||
}
|
||||
if (f) {
|
||||
delta = at - f->addr;
|
||||
|
|
@ -2790,25 +2782,17 @@ static void ds_print_offset(RzDisasmState *ds) {
|
|||
ds->lastflagitem.offset = f->addr;
|
||||
ds->lastflag = &ds->lastflagitem;
|
||||
label = f->name;
|
||||
} else {
|
||||
if (ds->show_reloff_flags) {
|
||||
/* XXX: this is wrong if starting to disasm after a flag */
|
||||
fi = rz_flag_get_i(core->flags, at);
|
||||
if (fi) {
|
||||
ds->lastflag = fi;
|
||||
}
|
||||
if (ds->lastflag) {
|
||||
if (ds->lastflag->offset == at) {
|
||||
delta = 0;
|
||||
} else {
|
||||
delta = at - ds->lastflag->offset;
|
||||
}
|
||||
} else {
|
||||
delta = at - core->offset;
|
||||
}
|
||||
if (ds->lastflag) {
|
||||
label = ds->lastflag->name;
|
||||
}
|
||||
} else if (ds->show_reloff_flags) {
|
||||
// Fall back to flags using unified API approach
|
||||
RzFlagItem *fi = rz_flag_get_at(core->flags, at, true);
|
||||
if (fi) {
|
||||
ds->lastflag = fi;
|
||||
}
|
||||
if (ds->lastflag) {
|
||||
delta = at - ds->lastflag->offset;
|
||||
label = ds->lastflag->name;
|
||||
} else {
|
||||
delta = at - core->offset;
|
||||
}
|
||||
}
|
||||
if (!ds->lastflag) {
|
||||
|
|
@ -3436,10 +3420,10 @@ static void ds_print_fcn_name(RzDisasmState *ds) {
|
|||
}
|
||||
if (delta > 0) {
|
||||
ds_begin_comment(ds);
|
||||
ds_comment(ds, true, "; %s+0x%x", f->name, delta);
|
||||
ds_comment(ds, true, "; %s+0x%" PFMT64x, f->name, (ut64)delta);
|
||||
} else if (delta < 0) {
|
||||
ds_begin_comment(ds);
|
||||
ds_comment(ds, true, "; %s-0x%x", f->name, -delta);
|
||||
ds_comment(ds, true, "; %s-0x%" PFMT64x, f->name, (ut64)(-delta));
|
||||
} else if ((!ds->core->vmode || (!ds->subjmp && !ds->subnames)) && (!ds->opstr || !strstr(ds->opstr, f->name))) {
|
||||
RzFlagItem *flag_sym;
|
||||
if (ds->core->vmode && (flag_sym = rz_flag_get_by_spaces(ds->core->flags, ds->analysis_op.jump, RZ_FLAGS_FS_SYMBOLS, NULL)) && flag_sym->demangled) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ rz_core_sources = [
|
|||
'analysis_objc.c',
|
||||
'analysis_tp.c',
|
||||
'basefind.c',
|
||||
'caddr.c',
|
||||
'cagraph.c',
|
||||
'cgraph.c',
|
||||
'canalysis.c',
|
||||
|
|
|
|||
|
|
@ -1038,11 +1038,8 @@ static void setprintmode(RzCore *core, int n) {
|
|||
|
||||
static bool fill_hist_offset(RzCore *core, RzLine *line, RzCoreSeekItem *csi) {
|
||||
ut64 off = csi->offset;
|
||||
RzFlagItem *f = rz_flag_get_at(core->flags, off, false);
|
||||
char *command = NULL;
|
||||
if (f && f->offset == off && f->offset > 0) {
|
||||
command = rz_str_newf("%s", f->name);
|
||||
} else {
|
||||
char *command = rz_core_addr_get_flag_offset(core->flags, off);
|
||||
if (!command) {
|
||||
command = rz_str_newf("0x%" PFMT64x, off);
|
||||
}
|
||||
if (!command) {
|
||||
|
|
@ -1236,17 +1233,9 @@ repeat:
|
|||
if (idx == skip) {
|
||||
cur_ref_addr = xaddr1;
|
||||
}
|
||||
RzAnalysisFunction *fun = rz_analysis_get_fcn_in(core->analysis, xaddr1, RZ_ANALYSIS_FCN_TYPE_NULL);
|
||||
char *name;
|
||||
if (fun) {
|
||||
name = rz_str_dup(fun->name);
|
||||
} else {
|
||||
RzFlagItem *f = rz_flag_get_at(core->flags, xaddr1, true);
|
||||
if (f) {
|
||||
name = rz_str_newf("%s + %" PFMT64d, f->name, xaddr1 - f->offset);
|
||||
} else {
|
||||
name = rz_str_dup("unk");
|
||||
}
|
||||
char *name = rz_core_addr_get_name_delta(core, xaddr1);
|
||||
if (!name) {
|
||||
name = rz_str_dup("unk");
|
||||
}
|
||||
if (w > 45) {
|
||||
if (strlen(name) > w - 45) {
|
||||
|
|
@ -3142,7 +3131,6 @@ static void visual_flagzone(RzCore *core) {
|
|||
}
|
||||
|
||||
RZ_IPI void rz_core_visual_title(RzCore *core, int color) {
|
||||
bool showDelta = rz_config_get_b(core->config, "scr.slow");
|
||||
const char *BEGIN = core->cons->context->pal.prompt;
|
||||
const char *filename;
|
||||
char pos[512], bar[512], pcs[32];
|
||||
|
|
@ -3214,36 +3202,12 @@ RZ_IPI void rz_core_visual_title(RzCore *core, int color) {
|
|||
|
||||
{ /* get flag with delta */
|
||||
ut64 addr = core->offset + (core->print->cur_enabled ? core->print->cur : 0);
|
||||
/* TODO: we need a helper into rz_flags to do that */
|
||||
RzFlagItem *f = NULL;
|
||||
if (rz_flag_space_push(core->flags, RZ_FLAGS_FS_SYMBOLS)) {
|
||||
f = rz_flag_get_at(core->flags, addr, showDelta);
|
||||
rz_flag_space_pop(core->flags);
|
||||
}
|
||||
if (!f) {
|
||||
f = rz_flag_get_at(core->flags, addr, showDelta);
|
||||
}
|
||||
if (f) {
|
||||
if (f->offset == addr || !f->offset) {
|
||||
snprintf(pos, sizeof(pos), "@ %s", f->name);
|
||||
} else {
|
||||
snprintf(pos, sizeof(pos), "@ %s+%d # 0x%" PFMT64x,
|
||||
f->name, (int)(addr - f->offset), addr);
|
||||
}
|
||||
char *name_delta = rz_core_addr_get_flag_offset(core->flags, addr);
|
||||
if (name_delta) {
|
||||
snprintf(pos, sizeof(pos), "@ %s", name_delta);
|
||||
free(name_delta);
|
||||
} else {
|
||||
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);
|
||||
if (fcn) {
|
||||
int delta = addr - fcn->addr;
|
||||
if (delta > 0) {
|
||||
snprintf(pos, sizeof(pos), "@ %s+%d", fcn->name, delta);
|
||||
} else if (delta < 0) {
|
||||
snprintf(pos, sizeof(pos), "@ %s%d", fcn->name, delta);
|
||||
} else {
|
||||
snprintf(pos, sizeof(pos), "@ %s", fcn->name);
|
||||
}
|
||||
} else {
|
||||
pos[0] = 0;
|
||||
}
|
||||
pos[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -200,6 +200,49 @@ typedef struct {
|
|||
char *cmd;
|
||||
} RzCoreGadget;
|
||||
|
||||
/**
|
||||
* \brief Options for controlling address description output.
|
||||
*
|
||||
* This structure contains flags that control how addresses are described
|
||||
* and formatted. It can be passed to rz_core_addr_describe() and related
|
||||
* functions to customize the output.
|
||||
*/
|
||||
typedef struct rz_core_addr_options_t {
|
||||
bool show_offset; ///< Show the address offset if no name is found
|
||||
bool prefer_function; ///< Prefer function names over flag names
|
||||
bool show_flag; ///< Include flag names in the description
|
||||
bool use_decimal; ///< Use decimal instead of hexadecimal for offsets
|
||||
bool show_color; ///< Include color codes in the output
|
||||
bool show_source_info; ///< Include source file/line information
|
||||
bool use_realnames; ///< Use realnames for flags instead of names
|
||||
st64 max_flag_delta; ///< Maximum delta from flag offset to show (0 = unlimited, negative = use default 8192)
|
||||
bool use_spaces_around_delta; ///< Use spaces around +/- in delta format (e.g., "sym + 10" vs "sym+10")
|
||||
} RzCoreAddrOptions;
|
||||
|
||||
/**
|
||||
* \brief Description of an address with human-readable information
|
||||
*
|
||||
* This structure contains comprehensive information about an address,
|
||||
* including its relation to functions, flags, and source code.
|
||||
*/
|
||||
typedef struct rz_core_addr_t {
|
||||
ut64 addr; ///< The address being described
|
||||
|
||||
char *name; ///< Combined name (deprecated, use fcn_name or flag_name)
|
||||
|
||||
char *fcn_name; ///< Name of the function containing the address
|
||||
ut64 fcn_addr; ///< Address of the function
|
||||
st64 fcn_delta; ///< Delta from function start (addr - fcn_addr)
|
||||
|
||||
char *flag_name; ///< Name of the flag at or before the address
|
||||
ut64 flag_offset; ///< Offset of the flag
|
||||
st64 flag_delta; ///< Delta from flag (addr - flag_offset)
|
||||
|
||||
char *source_file; ///< Source file name (if debug info available)
|
||||
ut32 source_line; ///< Source line number (0 if unknown)
|
||||
ut32 source_column; ///< Source column number (0 if unknown)
|
||||
} RzCoreAddr;
|
||||
|
||||
typedef struct rz_core_task_t RzCoreTask;
|
||||
|
||||
/**
|
||||
|
|
@ -595,6 +638,42 @@ RZ_API RzCmdStatus rz_core_debug_plugins_print(RZ_NONNULL RZ_BORROW RzCore *core
|
|||
RZ_API void rz_core_debug_map_update_flags(RzCore *core);
|
||||
RZ_API void rz_core_debug_map_print(RzCore *core, ut64 addr, RzCmdStateOutput *state);
|
||||
|
||||
/**
|
||||
* \defgroup caddr Address Description API
|
||||
* \brief Unified API for describing addresses in human-readable formats
|
||||
*
|
||||
* This API provides functions to generate human-readable descriptions of memory
|
||||
* addresses, combining information from flags, functions, and debug symbols.
|
||||
* It supports various output formats including name+offset notation (e.g., "main+0x10"),
|
||||
* source file information, and JSON output.
|
||||
*
|
||||
* Example usage:
|
||||
* \code
|
||||
* char *desc = rz_core_addr_get_name_delta(core, 0x401010);
|
||||
* // Returns "main+16" or "0x401010" if no symbol found
|
||||
* rz_cons_printf("Address: %s\n", desc);
|
||||
* free(desc);
|
||||
* \endcode
|
||||
*
|
||||
*/
|
||||
|
||||
RZ_API RZ_OWN RzCoreAddrOptions *rz_core_addr_options_new(void);
|
||||
RZ_API void rz_core_addr_options_free(RZ_NULLABLE RzCoreAddrOptions *opts);
|
||||
RZ_API void rz_core_addr_free(RZ_NULLABLE RzCoreAddr *desc);
|
||||
RZ_API RZ_OWN RzCoreAddr *rz_core_addr_describe(RZ_NONNULL RzCore *core, ut64 addr, RZ_NULLABLE const RzCoreAddrOptions *opts);
|
||||
RZ_API RZ_OWN char *rz_core_addr_to_string(RZ_NONNULL const RzCoreAddr *desc, RZ_NULLABLE const RzCoreAddrOptions *opts);
|
||||
RZ_API RZ_OWN char *rz_core_addr_describe_string(RZ_NONNULL RzCore *core, ut64 addr, RZ_NULLABLE const RzCoreAddrOptions *opts);
|
||||
RZ_API RZ_OWN char *rz_core_addr_get_name_delta(RZ_NONNULL RzCore *core, ut64 addr);
|
||||
RZ_API RZ_OWN RzCoreAddr *rz_core_addr_describe_with_function(RZ_NONNULL RzCore *core, ut64 addr);
|
||||
RZ_API RZ_OWN RzCoreAddr *rz_core_addr_describe_with_source(RZ_NONNULL RzCore *core, ut64 addr);
|
||||
RZ_API RZ_OWN char *rz_core_addr_format_for_display(RZ_NULLABLE RzPrint *print, ut64 addr, RZ_NULLABLE const RzCoreAddrOptions *opts);
|
||||
RZ_API void rz_core_addr_to_pj(RZ_NONNULL PJ *pj, RZ_NONNULL const RzCoreAddr *desc, RZ_NULLABLE const RzCoreAddrOptions *opts);
|
||||
RZ_API void rz_core_addr_describe_pj(RZ_NONNULL RzCore *core, RZ_NONNULL PJ *pj, ut64 addr, RZ_NULLABLE const RzCoreAddrOptions *opts);
|
||||
RZ_API bool rz_core_addr_get_reloff_info(RZ_NONNULL RzCore *core, ut64 addr, bool prefer_function, bool use_flags, RZ_OUT RZ_NULLABLE char **out_name, RZ_OUT RZ_NULLABLE st64 *out_delta);
|
||||
RZ_API RZ_OWN char *rz_core_addr_get_function_offset(RZ_NONNULL RzCore *core, ut64 addr);
|
||||
RZ_API RZ_OWN char *rz_core_addr_get_flag_offset(RZ_NONNULL RzFlag *flags, ut64 addr);
|
||||
RZ_API RZ_OWN char *rz_core_addr_get_flag_offset_prompt(RZ_NONNULL RzFlag *flags, ut64 addr);
|
||||
|
||||
/* chash.c */
|
||||
RZ_API RzCmdStatus rz_core_hash_plugins_print(RZ_NONNULL RZ_BORROW RzHash *hash, RZ_OUT RzCmdStateOutput *state);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#endif
|
||||
/* BEGIN DEFINES */
|
||||
// clang-format off
|
||||
#mesondefine ENABLE_FDOPEN
|
||||
#mesondefine HAVE___PROGNAME
|
||||
#mesondefine HAVE__CLOSE
|
||||
#mesondefine HAVE__DUP
|
||||
|
|
|
|||
|
|
@ -82,6 +82,14 @@ foreach fcn : underscore_functions
|
|||
conf_data.set10('HAVE__@0@'.format(fcn.to_upper()), true)
|
||||
endif
|
||||
endforeach
|
||||
|
||||
# Enable fdopen support if fdopen or _fdopen is available
|
||||
# This is needed for zip_fdopen.c to compile without unused variable warnings
|
||||
# Check for fdopen (POSIX/Linux/macOS) or _fdopen (Windows)
|
||||
if cc.has_function('fdopen') or cc.has_function('fdopen', prefix: '#include <stdio.h>') or cc.has_function('_fdopen') or cc.has_function('_fdopen', prefix: '#include <stdio.h>')
|
||||
conf_data.set10('ENABLE_FDOPEN', true)
|
||||
endif
|
||||
|
||||
foreach hdr : headers
|
||||
if cc.has_header(hdr)
|
||||
conf_data.set10('HAVE_@0@'.format(hdr.underscorify().to_upper()), true)
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ if get_option('enable_tests')
|
|||
'config',
|
||||
'cons',
|
||||
'contrbtree',
|
||||
'core_addr',
|
||||
'core_analysis_stats',
|
||||
'core_bin',
|
||||
'core_cmd',
|
||||
|
|
|
|||
258
test/unit/test_core_addr.c
Normal file
258
test/unit/test_core_addr.c
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
// SPDX-FileCopyrightText: 2024 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_core.h>
|
||||
#include "minunit.h"
|
||||
|
||||
bool test_rz_core_addr_options_new(void) {
|
||||
RzCoreAddrOptions *opts = rz_core_addr_options_new();
|
||||
mu_assert_notnull(opts, "options should be allocated");
|
||||
mu_assert_true(opts->show_offset, "show_offset should default to true");
|
||||
mu_assert_true(opts->prefer_function, "prefer_function should default to true");
|
||||
mu_assert_true(opts->show_flag, "show_flag should default to true");
|
||||
mu_assert_true(opts->use_decimal, "use_decimal should default to true");
|
||||
mu_assert_false(opts->show_color, "show_color should default to false");
|
||||
mu_assert_false(opts->show_source_info, "show_source_info should default to false");
|
||||
mu_assert_false(opts->use_realnames, "use_realnames should default to false");
|
||||
mu_assert_eq(opts->max_flag_delta, 0, "max_flag_delta should default to 0 (unlimited)");
|
||||
mu_assert_false(opts->use_spaces_around_delta, "use_spaces_around_delta should default to false");
|
||||
rz_core_addr_options_free(opts);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_core_addr_describe_basic(void) {
|
||||
RzCore *core = rz_core_new();
|
||||
mu_assert_notnull(core, "core should be allocated");
|
||||
|
||||
// Add a flag for testing
|
||||
rz_flag_set(core->flags, "sym.main", 0x1000, 10);
|
||||
|
||||
// Test basic description
|
||||
RzCoreAddr *desc = rz_core_addr_describe(core, 0x1000, NULL);
|
||||
mu_assert_notnull(desc, "description should be allocated");
|
||||
mu_assert_eq(desc->addr, 0x1000, "addr should match");
|
||||
mu_assert_notnull(desc->flag_name, "flag_name should be set");
|
||||
mu_assert_streq(desc->flag_name, "sym.main", "flag_name should match");
|
||||
mu_assert_eq(desc->flag_delta, 0, "flag_delta should be 0");
|
||||
rz_core_addr_free(desc);
|
||||
|
||||
// Test with offset
|
||||
desc = rz_core_addr_describe(core, 0x1005, NULL);
|
||||
mu_assert_notnull(desc, "description should be allocated");
|
||||
mu_assert_eq(desc->addr, 0x1005, "addr should match");
|
||||
mu_assert_notnull(desc->flag_name, "flag_name should be set");
|
||||
mu_assert_streq(desc->flag_name, "sym.main", "flag_name should match");
|
||||
mu_assert_eq(desc->flag_delta, 5, "flag_delta should be 5");
|
||||
rz_core_addr_free(desc);
|
||||
|
||||
// Test with address BEFORE any flag (should have no flag)
|
||||
desc = rz_core_addr_describe(core, 0x500, NULL);
|
||||
mu_assert_notnull(desc, "description should be allocated");
|
||||
mu_assert_eq(desc->addr, 0x500, "addr should match");
|
||||
mu_assert_null(desc->flag_name, "flag_name should be NULL for address before any flag");
|
||||
rz_core_addr_free(desc);
|
||||
|
||||
rz_core_free(core);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_core_addr_to_string(void) {
|
||||
RzCoreAddr desc = {
|
||||
.addr = 0x1005,
|
||||
.flag_name = "sym.main",
|
||||
.flag_offset = 0x1000,
|
||||
.flag_delta = 5
|
||||
};
|
||||
|
||||
// Test hex format
|
||||
RzCoreAddrOptions opts = {
|
||||
.show_offset = true,
|
||||
.prefer_function = false,
|
||||
.show_flag = true,
|
||||
.use_decimal = false,
|
||||
.show_color = false,
|
||||
.show_source_info = false,
|
||||
.use_realnames = false
|
||||
};
|
||||
|
||||
char *str = rz_core_addr_to_string(&desc, &opts);
|
||||
mu_assert_notnull(str, "string should be allocated");
|
||||
mu_assert_streq(str, "sym.main+0x5", "string should be name+offset");
|
||||
free(str);
|
||||
|
||||
// Test decimal format
|
||||
opts.use_decimal = true;
|
||||
str = rz_core_addr_to_string(&desc, &opts);
|
||||
mu_assert_notnull(str, "string should be allocated");
|
||||
mu_assert_streq(str, "sym.main+5", "string should be name+decimal offset");
|
||||
free(str);
|
||||
|
||||
// Test with zero delta
|
||||
desc.flag_delta = 0;
|
||||
str = rz_core_addr_to_string(&desc, &opts);
|
||||
mu_assert_notnull(str, "string should be allocated");
|
||||
mu_assert_streq(str, "sym.main", "string should be just name");
|
||||
free(str);
|
||||
|
||||
// Test with no name
|
||||
RzCoreAddr desc2 = {
|
||||
.addr = 0x2000,
|
||||
.flag_name = NULL
|
||||
};
|
||||
opts.use_decimal = false;
|
||||
str = rz_core_addr_to_string(&desc2, &opts);
|
||||
mu_assert_notnull(str, "string should be allocated");
|
||||
mu_assert_streq(str, "0x2000", "string should be hex address");
|
||||
free(str);
|
||||
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_core_addr_get_name_delta(void) {
|
||||
RzCore *core = rz_core_new();
|
||||
mu_assert_notnull(core, "core should be allocated");
|
||||
|
||||
// Add flags for testing
|
||||
rz_flag_set(core->flags, "sym.main", 0x1000, 10);
|
||||
rz_flag_set(core->flags, "sym.helper", 0x2000, 20);
|
||||
|
||||
// Test exact match
|
||||
char *str = rz_core_addr_get_name_delta(core, 0x1000);
|
||||
mu_assert_notnull(str, "string should be allocated");
|
||||
mu_assert_streq(str, "sym.main", "string should be flag name");
|
||||
free(str);
|
||||
|
||||
// Test with offset (uses decimal format by default)
|
||||
str = rz_core_addr_get_name_delta(core, 0x100a);
|
||||
mu_assert_notnull(str, "string should be allocated");
|
||||
mu_assert_streq(str, "sym.main+10", "string should be name+offset");
|
||||
free(str);
|
||||
|
||||
// Test no match - returns hex address string when no flag found (hex for clarity)
|
||||
str = rz_core_addr_get_name_delta(core, 0x500);
|
||||
mu_assert_notnull(str, "string should be allocated for unknown address");
|
||||
mu_assert_streq(str, "0x500", "string should be hex address when no flag found");
|
||||
free(str);
|
||||
|
||||
rz_core_free(core);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_core_addr_with_function(void) {
|
||||
RzCore *core = rz_core_new();
|
||||
mu_assert_notnull(core, "core should be allocated");
|
||||
|
||||
// Create a function for testing
|
||||
RzAnalysisFunction *fcn = rz_analysis_create_function(core->analysis, "main", 0x1000, RZ_ANALYSIS_FCN_TYPE_FCN);
|
||||
mu_assert_notnull(fcn, "function should be created");
|
||||
|
||||
// Test function preference - test at function start address
|
||||
// (rz_analysis_get_fcn_in requires basic blocks, so use rz_analysis_get_function_at)
|
||||
RzCoreAddrOptions opts = {
|
||||
.show_offset = true,
|
||||
.prefer_function = true,
|
||||
.show_flag = true,
|
||||
.use_decimal = false,
|
||||
.show_color = false,
|
||||
.show_source_info = false,
|
||||
.use_realnames = false
|
||||
};
|
||||
|
||||
RzCoreAddr *desc = rz_core_addr_describe(core, 0x1000, &opts);
|
||||
mu_assert_notnull(desc, "description should be allocated");
|
||||
mu_assert_notnull(desc->fcn_name, "fcn_name should be set");
|
||||
mu_assert_streq(desc->fcn_name, "main", "fcn_name should match");
|
||||
mu_assert_eq(desc->fcn_delta, 0, "fcn_delta should be 0 at function start");
|
||||
rz_core_addr_free(desc);
|
||||
|
||||
rz_core_free(core);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_core_addr_describe_pj(void) {
|
||||
RzCore *core = rz_core_new();
|
||||
mu_assert_notnull(core, "core should be allocated");
|
||||
|
||||
// Add a flag for testing
|
||||
rz_flag_set(core->flags, "sym.test", 0x1000, 10);
|
||||
|
||||
PJ *pj = pj_new();
|
||||
mu_assert_notnull(pj, "pj should be allocated");
|
||||
|
||||
rz_core_addr_describe_pj(core, pj, 0x1005, NULL);
|
||||
|
||||
char *json_str = pj_drain(pj);
|
||||
mu_assert_notnull(json_str, "json string should be allocated");
|
||||
// Should contain addr and flag info
|
||||
mu_assert("should contain addr", strstr(json_str, "\"addr\"") != NULL);
|
||||
mu_assert("should contain flag_name", strstr(json_str, "\"flag_name\"") != NULL);
|
||||
free(json_str);
|
||||
|
||||
rz_core_free(core);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_core_addr_max_flag_delta(void) {
|
||||
RzCore *core = rz_core_new();
|
||||
mu_assert_notnull(core, "core should be allocated");
|
||||
|
||||
// Add a flag far away from test address
|
||||
rz_flag_set(core->flags, "sym.distant", 0x1000, 10);
|
||||
|
||||
// Test with unlimited delta (0) - should find the flag
|
||||
RzCoreAddrOptions opts_unlimited = {
|
||||
.show_offset = true,
|
||||
.show_flag = true,
|
||||
.max_flag_delta = 0 // unlimited
|
||||
};
|
||||
RzCoreAddr *desc = rz_core_addr_describe(core, 0x10000, &opts_unlimited);
|
||||
mu_assert_notnull(desc, "description should be allocated");
|
||||
mu_assert_notnull(desc->flag_name, "flag_name should be set with unlimited delta");
|
||||
mu_assert_streq(desc->flag_name, "sym.distant", "flag_name should match");
|
||||
rz_core_addr_free(desc);
|
||||
|
||||
// Test with default 8192 limit (-1) - should NOT find the flag (delta too large)
|
||||
RzCoreAddrOptions opts_default = {
|
||||
.show_offset = true,
|
||||
.show_flag = true,
|
||||
.max_flag_delta = -1 // default 8192 limit
|
||||
};
|
||||
desc = rz_core_addr_describe(core, 0x10000, &opts_default);
|
||||
mu_assert_notnull(desc, "description should be allocated");
|
||||
mu_assert_null(desc->flag_name, "flag_name should be NULL with 8192 limit (delta too large)");
|
||||
rz_core_addr_free(desc);
|
||||
|
||||
// Test with small delta within limit - should find the flag
|
||||
desc = rz_core_addr_describe(core, 0x1100, &opts_default);
|
||||
mu_assert_notnull(desc, "description should be allocated");
|
||||
mu_assert_notnull(desc->flag_name, "flag_name should be set (delta within 8192)");
|
||||
mu_assert_eq(desc->flag_delta, 0x100, "flag_delta should be 0x100");
|
||||
rz_core_addr_free(desc);
|
||||
|
||||
// Test with custom limit
|
||||
RzCoreAddrOptions opts_custom = {
|
||||
.show_offset = true,
|
||||
.show_flag = true,
|
||||
.max_flag_delta = 100 // small custom limit
|
||||
};
|
||||
desc = rz_core_addr_describe(core, 0x1100, &opts_custom);
|
||||
mu_assert_notnull(desc, "description should be allocated");
|
||||
mu_assert_null(desc->flag_name, "flag_name should be NULL with 100 limit (delta 0x100 > 100)");
|
||||
rz_core_addr_free(desc);
|
||||
|
||||
rz_core_free(core);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool all_tests() {
|
||||
mu_run_test(test_rz_core_addr_options_new);
|
||||
mu_run_test(test_rz_core_addr_describe_basic);
|
||||
mu_run_test(test_rz_core_addr_to_string);
|
||||
mu_run_test(test_rz_core_addr_get_name_delta);
|
||||
mu_run_test(test_rz_core_addr_with_function);
|
||||
mu_run_test(test_rz_core_addr_describe_pj);
|
||||
mu_run_test(test_rz_core_addr_max_flag_delta);
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
||||
mu_main(all_tests)
|
||||
Loading…
Reference in a new issue