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:
IndAlok 2025-12-10 16:41:31 +05:30 committed by GitHub
parent d3087cc148
commit 666d0ba8a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 1171 additions and 202 deletions

754
librz/core/caddr.c Normal file
View 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);
}

View file

@ -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);

View file

@ -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) {

View file

@ -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;

View file

@ -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;
}

View file

@ -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) {

View file

@ -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) {

View file

@ -16,6 +16,7 @@ rz_core_sources = [
'analysis_objc.c',
'analysis_tp.c',
'basefind.c',
'caddr.c',
'cagraph.c',
'cgraph.c',
'canalysis.c',

View file

@ -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;
}
}

View file

@ -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);

View file

@ -5,6 +5,7 @@
#endif
/* BEGIN DEFINES */
// clang-format off
#mesondefine ENABLE_FDOPEN
#mesondefine HAVE___PROGNAME
#mesondefine HAVE__CLOSE
#mesondefine HAVE__DUP

View file

@ -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)

View file

@ -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
View 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)