/* * Interactive disassembler (IDA). * Copyright (c) 2016-2020 Hex-Rays * ALL RIGHTS RESERVED. * * Module independent exception description */ #ifndef TRYBLKS_HPP #define TRYBLKS_HPP /*! \file tryblks.hpp * * \brief Architecture independent exception handling info. * * Try blocks have the following general properties: * - An try block specifies a possibly fragmented guarded code region. * - Each try block has always at least one catch/except block description * - Each catch block contains its boundaries and a filter. * - Additionally a catch block can hold sp adjustment and the offset to the * exception object offset (C++). * - Try blocks can be nested. Nesting is automatically calculated at the retrieval time. * - There may be (nested) multiple try blocks starting at the same address. * * See examples in tests/input/src/eh_tests. * */ // We use end_ea=BADADDR if the exact boundaries are unknown of any range. //---------------------------------------------------------------------------- // An exception handler clause (the body of __except or catch statement) struct try_handler_t : public rangevec_t { sval_t disp; // displacement to the stack region of the guarded region. // if it is valid, it is fpreg relative. // -1 means unknown. int fpreg; // frame register number used in handler. -1 means none. try_handler_t() : disp(-1), fpreg(-1) {} void clear(void) { rangevec_t::clear(); disp = -1; fpreg = -1; } }; DECLARE_TYPE_AS_MOVABLE(try_handler_t); //---------------------------------------------------------------------------- // __except() {} statement struct seh_t : public try_handler_t { rangevec_t filter; // boundaries of the filter callback. if filter is empty, ea_t seh_code; // then use seh_code #define SEH_CONTINUE BADADDR // EXCEPTION_CONTINUE_EXECUTION (-1) #define SEH_SEARCH ea_t(0) // EXCEPTION_CONTINUE_SEARCH (0) (alias of __finally) #define SEH_HANDLE ea_t(1) // EXCEPTION_EXECUTE_HANDLER (1) void clear(void) { try_handler_t::clear(); filter.clear(); seh_code = SEH_CONTINUE; } }; DECLARE_TYPE_AS_MOVABLE(seh_t); //---------------------------------------------------------------------------- // catch() {} statement struct catch_t : public try_handler_t { sval_t obj; // fpreg relative displacement to the exception object. -1 if unknown. sval_t type_id; // the type caught by this catch. -1 means "catch(...)" #define CATCH_ID_ALL sval_t(-1) // catch(...) #define CATCH_ID_CLEANUP sval_t(-2) // a cleanup handler invoked if exception occures catch_t() : obj(-1), type_id(-1) {} }; DECLARE_TYPE_AS_MOVABLE(catch_t); typedef qvector catchvec_t; //---------------------------------------------------------------------------- class tryblk_t : public rangevec_t // block guarded by try/__try {...} statements { #ifndef SWIG char reserve[qmax(sizeof(catchvec_t), sizeof(seh_t))]; // seh_t or catchvec_t #endif uchar cb; // size of tryblk_t uchar kind; // one of the following kinds #define TB_NONE 0 // empty #define TB_SEH 1 // MS SEH __try/__except/__finally #define TB_CPP 2 // C++ language try/catch public: uchar level; // nesting level, calculated by get_tryblks() // C++ try/catch block (TB_CPP) catchvec_t &cpp() { return *(( catchvec_t *)reserve); } const catchvec_t &cpp() const { return *((const catchvec_t *)reserve); } // SEH __except/__finally case (TB_SEH) seh_t &seh() { return *(( seh_t *)reserve); } const seh_t &seh() const { return *((const seh_t *)reserve); } tryblk_t() : rangevec_t(), cb(sizeof(*this)), kind(TB_NONE), level(0) { reserve[0] = '\0'; } ~tryblk_t() { clear(); } tryblk_t(const tryblk_t &r) : rangevec_t(), kind(TB_NONE) { *this = r; } uchar get_kind(void) const { return kind; } bool empty(void) const { return kind == TB_NONE; } bool is_seh(void) const { return kind == TB_SEH; } bool is_cpp(void) const { return kind == TB_CPP; } //------------------------------------------------------------------------- tryblk_t &operator=(const tryblk_t &r) { if ( this != &r ) // don't copy yourself { if ( kind != TB_NONE ) clear(); kind = r.kind; level = r.level; rangevec_t::operator=(r); if ( kind == TB_SEH ) new (reserve) seh_t(r.seh()); else if ( kind == TB_CPP ) new (reserve) catchvec_t(r.cpp()); } return *this; } //------------------------------------------------------------------------- void clear(void) { if ( kind == TB_CPP ) cpp().~catchvec_t(); else if ( kind == TB_SEH ) seh().~seh_t(); kind = TB_NONE; } //------------------------------------------------------------------------- seh_t &set_seh(void) { if ( kind != TB_SEH ) { clear(); new (reserve) seh_t; kind = TB_SEH; } else { seh().clear(); } return seh(); } //------------------------------------------------------------------------- catchvec_t &set_cpp(void) { if ( kind != TB_CPP ) { clear(); new (reserve) catchvec_t; kind = TB_CPP; } else { cpp().clear(); } return cpp(); } }; DECLARE_TYPE_AS_MOVABLE(tryblk_t); typedef qvector tryblks_t; ///------------------------------------------------------------------------- /// Retrieve try block information from the specified address range. /// Try blocks are sorted by starting address and their nest levels calculated. /// \param tbv output buffer; may be NULL /// \param range address range to change /// \return number of found try blocks idaman size_t ida_export get_tryblks(tryblks_t *tbv, const range_t &range); /// Delete try block information in the specified range. /// \param range the range to be cleared idaman void ida_export del_tryblks(const range_t &range); /// Add one try block information. /// \param tb try block to add. /// \return error code; 0 means good idaman int ida_export add_tryblk(const tryblk_t &tb); /// \defgroup TBERR_ Try block handling error codes //@{ #define TBERR_OK 0 ///< ok #define TBERR_START 1 ///< bad start address #define TBERR_END 2 ///< bad end address #define TBERR_ORDER 3 ///< bad address order #define TBERR_EMPTY 4 ///< empty try block #define TBERR_KIND 5 ///< illegal try block kind #define TBERR_NO_CATCHES 6 ///< no catch blocks at all #define TBERR_INTERSECT 7 ///< range would intersect inner tryblk //@} /// Find the start address of the system eh region including the argument. /// \param ea search address /// \return start address of surrounding tryblk, otherwise BADADDR idaman ea_t ida_export find_syseh(ea_t ea); #endif // TRYBLKS_HPP