Repository URL to install this package:
|
Version:
9.0~240925-3.fc42 ▾
|
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2024 Hex-Rays
* ALL RIGHTS RESERVED.
*
*/
#ifndef FIXUP_HPP
#define FIXUP_HPP
#include <nalt.hpp>
#include <segment.hpp>
/*! \file fixup.hpp
\brief Functions that deal with fixup information
A loader should setup fixup information using set_fixup().
*/
//--------------------------------------------------------------------------
/// Fixup information structure
typedef uint16 fixup_type_t; ///< see \ref fixup_type_t
/// \defgroup fixup_type_t Types of fixups
/// Fixup may be of standard or custom type. Standard types fall into
/// range 1..FIXUP_CUSTOM-1 and custom types fall into range
/// FIXUP_CUSTOM..MAX_UINT16.
/// \note Fixup type 0 is unused.
/// \name Fixup standard types
///@{
#define FIXUP_OFF8 13 ///< 8-bit offset
#define FIXUP_OFF16 1 ///< 16-bit offset
#define FIXUP_SEG16 2 ///< 16-bit base--logical segment base (selector)
#define FIXUP_PTR16 3 ///< 32-bit long pointer (16-bit base:16-bit
///< offset)
#define FIXUP_OFF32 4 ///< 32-bit offset
#define FIXUP_PTR32 5 ///< 48-bit pointer (16-bit base:32-bit offset)
#define FIXUP_HI8 6 ///< high 8 bits of 16bit offset
#define FIXUP_HI16 7 ///< high 16 bits of 32bit offset
#define FIXUP_LOW8 8 ///< low 8 bits of 16bit offset
#define FIXUP_LOW16 9 ///< low 16 bits of 32bit offset
#define V695_FIXUP_VHIGH 10 ///< obsolete
#define V695_FIXUP_VLOW 11 ///< obsolete
#define FIXUP_OFF64 12 ///< 64-bit offset
#define FIXUP_OFF8S 14 ///< 8-bit signed offset
#define FIXUP_OFF16S 15 ///< 16-bit signed offset
#define FIXUP_OFF32S 16 ///< 32-bit signed offset
//#define FIXUP_ 0xE
#define FIXUP_CUSTOM 0x8000 ///< start of the custom types range
///@}
/// Is fixup processed by processor module?
inline THREAD_SAFE bool is_fixup_custom(fixup_type_t type)
{
return (type & FIXUP_CUSTOM) != 0;
}
/// \defgroup FIXUPF_ Fixup flags
/// Used by fixup_data_t
///@{
/// fixup is relative to the linear address `base`. Otherwise fixup is
/// relative to the start of the segment with `sel` selector.
#define FIXUPF_REL 0x0001
/// target is a location (otherwise - segment).
/// Use this bit if the target is a symbol rather than an offset from the
/// beginning of a segment.
#define FIXUPF_EXTDEF 0x0002
/// fixup is ignored by IDA
/// - disallows the kernel to convert operands
/// - this fixup is not used during output
#define FIXUPF_UNUSED 0x0004
/// fixup was not present in the input file
#define FIXUPF_CREATED 0x0008
/// additional flags. The bits from this mask are not stored in the database
/// and can be used by the loader at its discretion.
#define FIXUPF_LOADER_MASK 0xF0000000
///@}
struct fixup_handler_t;
struct fixup_data_t
{
protected:
fixup_type_t type; // fixup type
uint32 flags; // FIXUPF_... bits
uval_t base; // base for relative fixups
public:
sel_t sel; ///< selector of the target segment.
///< BADSEL means an absolute (zero based) target.
///< \sa FIXUPF_REL
ea_t off; ///< target offset
///< \note The target is calculated as
///< `get_base() + off`.
adiff_t displacement; ///< displacement (offset from the target)
public:
fixup_data_t()
: type(0),
flags(0),
base(0),
sel(BADSEL),
off(0),
displacement(0) {}
fixup_data_t(fixup_type_t type_, uint32 flags_ = 0)
: type(type_),
flags(flags_),
base(0),
sel(BADSEL),
off(0),
displacement(0) {}
/// Fixup type \ref fixup_type_t
fixup_type_t get_type(void) const { return type; }
void set_type(fixup_type_t type_) { type = type_; }
void set_type_and_flags(fixup_type_t type_, uint32 flags_ = 0)
{
type = type_;
flags = flags_;
}
bool is_custom(void) const; ///< \ref is_fixup_custom()
/// Fixup flags \ref FIXUPF_
uint32 get_flags() const { return flags; }
bool is_extdef(void) const { return (flags & FIXUPF_EXTDEF) != 0; }
void set_extdef(void) { flags |= FIXUPF_EXTDEF; }
void clr_extdef(void) { flags &= ~FIXUPF_EXTDEF; }
bool is_unused(void) const { return (flags & FIXUPF_UNUSED) != 0; }
void set_unused(void) { flags |= FIXUPF_UNUSED; }
void clr_unused(void) { flags &= ~FIXUPF_UNUSED; }
/// Is fixup relative?
bool has_base(void) const { return (flags & FIXUPF_REL) != 0; }
/// Is fixup artificial?
bool was_created(void) const { return (flags & FIXUPF_CREATED) != 0; }
/// Get base of fixup.
/// \note The target is calculated as `get_base() + off`.
/// \sa FIXUPF_REL
ea_t get_base() const
{
return has_base() ? base : sel != BADSEL ? sel2ea(sel) : 0;
}
/// Set base of fixup.
/// The target should be set before a call of this function.
void set_base(ea_t new_base)
{
ea_t target = get_base() + off;
flags |= FIXUPF_REL;
base = new_base;
off = target - base;
}
void set_sel(const segment_t *seg)
{
sel = seg == nullptr ? BADSEL : seg->sel;
}
/// Set selector of fixup to the target.
/// The target should be set before a call of this function.
void set_target_sel()
{
ea_t target = get_base() + off;
set_sel(getseg(target));
flags &= ~FIXUPF_REL;
base = 0; // just in case
off = target - get_base();
}
void set(ea_t source) const; ///< \ref set_fixup()
bool get(ea_t source); ///< \ref get_fixup()
/// \ref get_fixup_handler()
const fixup_handler_t *get_handler() const;
/// \ref get_fixup_desc()
const char *get_desc(qstring *buf, ea_t source) const;
// TODO rewrite to the inline implementation which uses
// fixup_handler_t::size
int calc_size() const; ///< \ref calc_fixup_size()
uval_t get_value(ea_t ea) const; ///< \ref get_fixup_value()
bool patch_value(ea_t ea) const; ///< \ref patch_fixup_value()
};
/// Get fixup information
idaman bool ida_export get_fixup(fixup_data_t *fd, ea_t source);
/// Check that a fixup exists at the given address
inline bool exists_fixup(ea_t source)
{
return get_fixup(nullptr, source);
}
/// Set fixup information. You should fill ::fixup_data_t and call this
/// function and the kernel will remember information in the database.
/// \param source the fixup source address, i.e. the address modified by
/// the fixup
/// \param fd fixup data
idaman void ida_export set_fixup(ea_t source, const fixup_data_t &fd);
/// Delete fixup information
idaman void ida_export del_fixup(ea_t source);
/// \name Enumerate addresses with fixup information:
///@{
/// Get the first address with fixup information
///
/// \return the first address with fixup information, or BADADDR
idaman ea_t ida_export get_first_fixup_ea(void);
/// Find next address with fixup information
///
/// \param ea current address
/// \return the next address with fixup information, or BADADDR
idaman ea_t ida_export get_next_fixup_ea(ea_t ea);
/// Find previous address with fixup information
///
/// \param ea current address
/// \return the previous address with fixup information, or BADADDR
idaman ea_t ida_export get_prev_fixup_ea(ea_t ea);
///@}
/// Get handler of standard or custom fixup
idaman const fixup_handler_t * ida_export get_fixup_handler(fixup_type_t type);
/// Use fixup information for an address.
/// This function converts item_ea flags to offsets/segments.
/// For undefined bytes, you may set item_ea == fixup_ea. In this case this
/// function will create an item (byte, word, dword) there.
/// \param item_ea start address of item to modify
/// \param fixup_ea address of fixup record
/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL one of the operands
/// \param is_macro is the instruction at 'item_ea' a macro?
/// if yes, then partial fixups (HIGH, LOW) won't be applied
/// \retval false no fixup at fixup_ea or it has #FIXUPF_UNUSED flag
/// \retval true ok, the fixup information was applied
idaman bool ida_export apply_fixup(ea_t item_ea, ea_t fixup_ea, int n, bool is_macro);
/// Get the operand value.
/// This function get fixup bytes from data or an instruction at `ea` and
/// convert them to the operand value (maybe partially).
/// It is opposite in meaning to the `patch_fixup_value()`.
/// For example, FIXUP_HI8 read a byte at `ea` and shifts it left by 8 bits,
/// or AArch64's custom fixup BRANCH26 get low 26 bits of the insn at `ea`
/// and shifts it left by 2 bits.
/// This function is mainly used to get a relocation addend.
/// \param ea address to get fixup bytes from, the size of the fixup
/// bytes depends on the fixup type.
/// \sa fixup_handler_t::size
/// \param type fixup type
/// \retval operand value
idaman uval_t ida_export get_fixup_value(ea_t ea, fixup_type_t type);
/// Patch the fixup bytes.
/// This function updates data or an instruction at `ea` to the fixup bytes.
/// For example, FIXUP_HI8 updates a byte at `ea` to the high byte of
/// `fd->off`, or AArch64's custom fixup BRANCH26 updates low 26 bits of the
/// insn at `ea` to the value of `fd->off` shifted right by 2.
/// \param ea address where data are changed, the size of the changed data
/// depends on the fixup type.
/// \sa fixup_handler_t::size
/// \param fd fixup data
/// \retval false the fixup bytes do not fit (e.g. `fd->off` is greater
/// than 0xFFFFFFC for BRANCH26). The database is changed
/// even in this case.
idaman bool ida_export patch_fixup_value(ea_t ea, const fixup_data_t &fd);
/// Get FIXUP description comment.
idaman const char *ida_export get_fixup_desc(
qstring *buf,
ea_t source,
const fixup_data_t &fd);
/// Calculate size of fixup in bytes (the number of bytes the fixup patches)
/// \retval -1 means error
idaman int ida_export calc_fixup_size(fixup_type_t type);
//--------------------------------------------------------------------------
// inline implementation
inline bool fixup_data_t::is_custom(void) const
{
return is_fixup_custom(type);
}
inline void fixup_data_t::set(ea_t source) const
{
set_fixup(source, *this);
}
inline bool fixup_data_t::get(ea_t source)
{
return get_fixup(this, source);
}
inline const char *fixup_data_t::get_desc(qstring *buf, ea_t source) const
{
return get_fixup_desc(buf, source, *this);
}
inline int fixup_data_t::calc_size() const
{
return calc_fixup_size(type);
}
inline uval_t fixup_data_t::get_value(ea_t ea) const
{
return get_fixup_value(ea, type);
}
inline bool fixup_data_t::patch_value(ea_t ea) const
{
return patch_fixup_value(ea, *this);
}
inline const fixup_handler_t *fixup_data_t::get_handler() const
{
return get_fixup_handler(type);
}
/// \name Custom fixups
/// Processor modules and plugins may register custom fixup handlers. File
/// loaders should use find_custom_fixup() function to find the handler
/// created by the processor module. The custom fixup handlers will be
/// unregistered automatically before the database gets closed.
///@{
//--------------------------------------------------------------------------
/// Implements the core behavior of a custom fixup
struct fixup_handler_t
{
int32 cbsize; ///< size of this structure
const char *name; ///< Format name, must be unique
uint32 props; ///< \ref FHF_
/// \defgroup FHF_ Fixup handler properties
/// Used by fixup_handler_t::props
///@{
#define FHF_VERIFY 0x0001 ///< verify that the value fits into WIDTH
///< bits. If this property is not set we
///< just truncate the value.
#define FHF_CODE 0x0002 ///< verify that ITEM_EA in std_apply() points
///< to an instruction.
#define FHF_FORCE_CODE 0x0004 ///< if ITEM_EA in std_apply() points to an
///< unknown item, then convert it to code.
///< this property is valid only with FHF_CODE.
#define FHF_ABS_OPVAL 0x0008 ///< create absolute refinfo in std_apply()
///< because the operand also has the absolute
///< value (usually for o_near operands)
#define FHF_SIGNED 0x0010 ///< the operand value is signed.
///< create a refinfo with REFINFO_SIGNEDOP in
///< std_apply()
///@}
/// Is the operand value signed?
bool is_signed() const { return (props & FHF_SIGNED) != 0; }
/// \defgroup fh_options Tuning options
///@{
/// The examples below show how these options work.
/// \sa std_patch_value() std_get_value()
uint8 size; ///< size in bytes
uint8 width; ///< number of significant bits before shifting
uint8 shift; ///< number of bits to shift right before patching.
///< The following should be true:
///< width - shift <= size * 8
uint8 rsrv4; // reserved
uint32 reftype; ///< reference info type and flags,
///< std_apply() produces an offset of this type
///@}
/// Apply a fixup: take it into account while analyzing the file.
/// Usually it consists of converting the operand into an offset expression.
/// \sa apply_fixup()
/// If this callback is not specified then std_apply() is used.
bool (idaapi *apply)(
const fixup_handler_t *fh,
ea_t item_ea,
ea_t fixup_ea,
int opnum,
bool is_macro,
const fixup_data_t &fd);
/// Get the operand value.
/// This callback is called from get_fixup_value().
/// \sa get_fixup_value()
/// If this callback is not specified then std_get_value() is used.
uval_t (idaapi *get_value)(const fixup_handler_t *fh, ea_t ea);
/// Patch the fixup bytes.
/// This callback is called from patch_fixup_value() or after changing the
/// fixup (e.g. after it was moved from one location to another).
/// If this callback is not specified then std_patch_value() is used.
/// \sa patch_fixup_value()
/// \retval false the fixup bytes do not fit. The database is changed
/// even in this case.
bool (idaapi *patch_value)(
const fixup_handler_t *fh,
ea_t ea,
const fixup_data_t &fd);
#ifndef SWIG
DECLARE_COMPARISONS(fixup_handler_t);
#endif
};
/// \name std_apply()
/// This internal function takes \ref fh_options and \ref FHF_ to convert
/// the fixup to an offset.
/// \name std_patch_value()
/// This internal function takes \ref fh_options and \ref FHF_ to determine
/// how to patch the bytes.
/// 1) it verifies that the fixup value fits to the fixup_handler_t::width
/// bits if the FHF_VERIFY property is specified,
/// 2) it discards bits that do not fit,
/// 3) it shifts the result right by fixup_handler_t::shift bits,
/// 4) it puts the result to the rightmost bits of fixup_handler_t::size
/// bytes at the given address.
/// For example, FIXUP_HI8 uses size = 1, and width = 16, and shift = 8, and
/// property FHF_VERIFY, or MIPS's custom fixup BRANCH26 uses size = 4,
/// and width = 28, and shift = 2.
/// In details:
/// a) size = 1, width = 16, shift = 8
/// - the value to patch is masked with 0xFFFF (width=16)
/// - then it is shifted right by 8 bits (shift=8)
/// - then the result is patched to the 8bit data at the fixup address
/// e.g.
/// 0xXX the original data
/// 0x1234 the value
/// 0x0012 the shifted value
/// 0x12 the patched data
/// b) size = 4, width = 28, shift = 2
/// - the value to patch is masked with 0xFFFFFFF (width=28)
/// - then it is shifted right by 2 bits (shift=2)
/// - then the result is patched in the low 26 bits of the 32bit
/// e.g.
/// 0x10000000 an instruction at the fixup address
/// 0x0000005C the value
/// 0x00000017 the shifted value
/// 0x10000017 the patched insn
/// \name std_get_value()
/// This internal function takes \ref fh_options to determine how to get the
/// operand value.
/// It is opposite in meaning to the std_patch_value().
/// 1) it gets the fixup_handler_t::size bytes at the given address,
/// 2) it shifts the result left by fixup_handler_t::shift bits,
/// 3) it returns the rightmost fixup_handler_t::width bits as a signed
/// value.
/// In details:
/// b) size = 4, width = 28, shift = 2
/// - it gets 4 bytes from the fixup address (the branch insn)
/// - then it shifts this dword left by 2 bits (shift=2)
/// - then the result is masked with 0xFFFFFFF (width=28)
/// e.g.
/// 0x10000017 the insn
/// 0x4000005C the unshifted value
/// 0x0000005C the masked result
/// Register a new custom fixup.
/// This function must be called by a processor module or plugin, but not
/// by a file loader. File loaders should use find_custom_fixup() function
/// to find the handler created by the processor module.
/// \return id of the new custom fixup handler with FIXUP_CUSTOM bit set or
/// 0 (e.g. when the custom fixup handler with the same name was
/// already registered).
idaman fixup_type_t ida_export register_custom_fixup(
const fixup_handler_t *cfh);
/// Unregister a new custom fixup format. Should be called by the processor
/// module before the database gets closed.
idaman bool ida_export unregister_custom_fixup(fixup_type_t type);
/// Get id of a custom fixup handler.
/// \param name name of the custom fixup handler
/// \return id with FIXUP_CUSTOM bit set or 0
idaman fixup_type_t ida_export find_custom_fixup(const char *name);
///@}
//--------------------------------------------------------------------------
/// Collect fixup records for the specified range.
/// Partially overlapping records will be reported too.
/// \return success (false means no fixup info have been found)
struct fixup_info_t
{
ea_t ea;
fixup_data_t fd;
};
DECLARE_TYPE_AS_MOVABLE(fixup_info_t);
typedef qvector<fixup_info_t> fixups_t;
idaman bool ida_export get_fixups(fixups_t *out, ea_t ea, asize_t size);
/// Does the specified address range contain any fixup information?
inline bool contains_fixups(ea_t ea, asize_t size)
{
return get_fixups(nullptr, ea, size);
}
/// Relocate the bytes with fixup information once more (generic function).
/// This function may be called from loader_t::move_segm() if it suits the goal.
/// If loader_t::move_segm is not defined then this function will be called
/// automatically when moving segments or rebasing the entire program.
/// Special parameter values (from = BADADDR, size = 0, to = delta) are used
/// when the function is called from rebase_program(delta).
idaman void ida_export gen_fix_fixups(ea_t from, ea_t to, asize_t size);
/// Handle two fixups in a macro.
/// We often combine two instruction that load parts of a value into one
/// macro instruction. For example:
/// \code
/// ARM: ADRP X0, #var@PAGE
/// ADD X0, X0, #var@PAGEOFF --> ADRL X0, var
/// MIPS: lui $v0, %hi(var)
/// addiu $v0, $v0, %lo(var) --> la $v0, var
/// \endcode
/// When applying the fixups that fall inside such a macro, we should convert
/// them to one refinfo. This function does exactly that.
/// It should be called from the apply() callback of a custom fixup.
/// \return success ('false' means that RI was not changed)
idaman bool ida_export handle_fixups_in_macro(
refinfo_t *ri,
ea_t ea,
fixup_type_t other,
uint32 macro_reft_and_flags);
#endif // FIXUP_HPP