573 lines
19 KiB
C++
573 lines
19 KiB
C++
/*
|
|
* Interactive disassembler (IDA).
|
|
* Copyright (c) 1990-2021 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 == NULL ? 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(NULL, 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 number of operand. may be 0, 1, 2, or OPND_ALL
|
|
/// \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(NULL, 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:
|
|
/// 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
|
|
/// 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
|