API 7.0 Porting Guide

clear IDA 7.0 SDK: Porting from IDA 4.9-6.x API to IDA 7.0 API

Introduction

The SDK now only supports the new 7.0 API in x64 mode. The old SDK 6.95 can be used to develop plugins for IDA 7.0-x86 (which is ABI-compatible with IDA 6.95).

While the API has been revamped somewhat, most basic concepts still hold.

There are still two variants of IDA: one supporting 32-bit (ea_t is 32-bit) and the other 64-bit address space (ea_t is 64-bit). IDA database extensions remain correspondingly '.idb' and '.i64'.

Naming of IDA binaries has been unified across all OS variants:

  • The IDA GUI binary has been renamed from 'idaq[.exe]' to just 'ida[.exe]'.

  • The IDA text-mode UI has been renamed from 'idaw.exe' (on Windows) and 'idal' (on Linux/Mac OS X) to 'idat[.exe]' on all platforms.

  • Plugins, loaders, processor modules, and the kernel now use standard OS-specific suffixes ('.dll', '.so', or '.dylib') instead of custom extensions.

General approaches that were taken when cleaning up the APIs:

  • Try to use descriptive names and drop old, cryptic abbreviations.

  • Rename functions using camelCase to snake_case (e.g. 'isFlow' -> 'is_flow').

  • Move output parameters to the front of the argument list.

  • Change input parameters to const references whenever possible.

  • Remove obsolete and deprecated functions.

  • Rename functioname2/3 to just functioname (e.g. 'validate_name3' -> 'validate_name').

  • Rename functions with 64 suffix to the main name (e.g. 'qfseek64' -> 'qfseek').

  • File offsets are 64-bit in all functions working with files.

  • Get rid of global variables (not complete, but we made good progress).

  • Most functions accepting a buffer and size (or limited to MAXSTR) now use 'qstring' or 'bytevec_t' instead (depending on the nature of the data).

  • Assume UTF-8 in most functions dealing with text.

  • Try to get rid of forced struct packing and rearrange fields to avoid unnecessary gaps as needed.

Porting

Common porting steps for plugins/loaders/modules:

  • Add __X64__ to the list of preprocessor defines. You still need to compile with or without __EA64__ defined to select between 32- and 64-bit address space.

  • If using custom build system, change output extension to OS-specific suffixes ('.dll', '.so', or '.dylib').

  • IDA library link path should start with x64 instead of x86.

Renamed/removed header files

Some headers have been renamed and/or removed:

original name
new name

ints.hpp

<removed>

sistack.h

<removed>

area.hpp

range.hpp

queue.hpp

problems.hpp

srarea.hpp

segregs.hpp

Commonly used renamed structs and fields

original name
new name

area_t

range_t

areavec_t

rangevec_t

endEA

end_ea

startEA

start_ea

area-related methods have been renamed too (e.g. 'prev_area' -> 'prev_range').

Porting plugins.

The plugin entry prototype has been changed from:

  • void idaapi run(int);

to:

  • bool idaapi run(size_t);

The input parameter is now of type 'size_t', which allows passing a pointer as the argument of run() for extra possibilities.

The rest of the plugin interface is unchanged.

Porting loaders

The prototype for 'accept_file()' has been changed from:

  • int idaapi accept_file(linput_t *li, char fileformatname[MAX_FILE_FORMAT_NAME], int n);

to:

  • int idaapi accept_file(qstring *fileformatname, qstring *processor, linput_t *li, const char *filename);

The desired processor may be returned in the 'processor' output parameter.

The return value has been extended with flags 'ACCEPT_ARCHIVE' and 'ACCEPT_CONTINUE'.

Loaders can also process and extract archives now. If you detect an archive, the return value for 'accept_file' should be ORed with the 'ACCEPT_ARCHIVE' flag. After extraction, all loaders are queried again, which means IDA can now handle multiply nested archives.

Non-archive loaders should extend the return value with the 'ACCEPT_CONTINUE' flag.

Porting processor modules

WARNING: The global variables 'cmd' and 'uFlag' are gone.

Most APIs return or accept an 'insn_t' structure with instruction details.

The 'processor_t' structure has had many unused and obsolete fields removed, such as 'flag2', 'rFiles', 'rFnames', 'rFdescs', and 'CPUregs'.

Most callbacks are now handled centrally via the 'notify()' function:

original name
new name

header

ev_out_header

footer

ev_out_footer

segstart

ev_out_segstart

segend

ev_out_segend

assumes

ev_out_assumes

u_ana

ev_ana_insn

u_emu

ev_emu_insn

u_out

ev_out_insn

u_outop

ev_out_operand

d_out

ev_out_data

cmp_opnd

ev_cmp_opnd

can_have_type

ev_can_have_type

is_far_jump

ev_is_far_jump

getreg

ev_getreg

ana.cpp

Change the prototype of 'ana' from:

  • int idaapi ana(void);

to:

  • int idaapi ana(insn_t *_insn);

You may then declare an 'insn_t' reference variable to simplify your code:

  • insn_t &insn = *_insn;

Then replace all uses of 'cmd' by 'insn'. You will likely need to pass 'insn' to other helper functions that used 'cmd'.

emu.cpp

Change the prototype of 'emu' from:

  • int idaapi emu(void);

to:

  • int idaapi emu(const insn_t &insn);

Then replace all uses of 'cmd' by 'insn'. You may need to adjust some code if it was relying on cmd being writeable.

out.cpp

The output functions now use a context structure ('outctx_t') instead of operating on a global buffer.

You must declare a class inheriting from 'outctx_t' and override its methods or add new ones for non-standard functions. For example:

class out_myproc_t : public outctx_t
{
  void outreg(int r) { out_register(ph.reg_names[r]); }
  void outmem(const op_t &x, ea_t ea);
  bool outbit(ea_t ea, int bit);

  bool out_operand(const op_t &x);
  void out_insn(void);
  void out_mnem(void);
}

Then use one of the two macros from idaidp.hpp:

  • DECLARE_OUT_FUNCS_WITHOUT_OUTMNEM(out_myproc_t)

or, if you implement 'out_mnem':

  • DECLARE_OUT_FUNCS(out_myproc_t)

Then prefix old function names with your class and rename them to match methods. For example, from:

  void idaapi out(void);
  void out_myproc_t::out(void);

to:

  bool idaapi outop(op_t &x);
  bool out_myproc_t::out_operand(const op_t &x);

Then remove calls to 'init_output_buffer()' and uses of the buffer variable.

Other changes that must be made are:

  • Replacing references to 'cmd' with 'insn';

  • Replacing term_output_buffer()/MakeLine() sequence with flush_outbuf().

Most of the other code can remain intact or can be replaced by the new helper functions.

For other output-related callbacks, convert them to take an 'outctx_t &ctx' parameter and use its methods. For example, from:

  • void idaapi header(void);

to:

  • void idaapi myproc_header(outctx_t &ctx)

See the changes to 'ua.hpp' below for more information on converting the functions.

Also, see the SDK samples for more ideas.

reg.cpp

Remove the old callbacks from the 'processor_t' structure and call them from the 'notify()' function instead. For example:

    case processor_t::ev_ana_insn:
      {
        insn_t *out = va_arg(va, insn_t *);
        return myproc_ana(out);
      }

For 'ev_out_insn', call 'out_insn()' generated by the macro in out.cpp:

    case processor_t::ev_out_insn:
      {
        outctx_t *ctx = va_arg(va, outctx_t *);
        out_insn(*ctx);
        return 1;
      }

Porting notifications

  • When hooking notifications, return 0 for "not handled" instead of 1 as before.

  • Many notifications had their arguments types and/or order changed. Double-check your handlers against the new headers.

  • Instead of calling ph.notify() or similar, prefer helper inline functions for additional type safety. For example, use 'ph.get_operand_string()' instead of 'ph.notify(processor_t::get_operand_string, ...)'.

  • Some IDP events have been moved to the IDB event group (see the table class="table table-sm" below), so they should be handled on the HT_IDB level. You will need to move the corresponding code from the IDP notification hooks to the IDB hooks.

original IDP event
new IDB notification

closebase

closebase

savebase

savebase

auto_empty

auto_empty

auto_empty_finally

auto_empty_finally

determined_main

determined_main

load_idasgn

idasgn_loaded

kernel_config_loaded

kernel_config_loaded

loader_finished

loader_finished

preprocess_chart

flow_chart_created

setsgr

sgr_changed

set_compiler

compiler_changed

move_segm

segm_moved

extlang_changed

extlang_changed

make_code

make_code

make_data

make_data

renamed

renamed

add_func

func_added

del_func

deleting_func

set_func_start

set_func_start

set_func_end

set_func_end

UI: Porting choosers

  • Make a new class derived from 'chooser_t' or 'chooser_multi_t'. Its fields are similar to arguments of 'choose2()' from IDA 6.95.

  • You should implement at least 2 methods:

  • 'get_count()', and

  • 'get_row()'.

The 'get_row()' method combines 3 methods of 6.95's old 'chooser_info_t':

  • 'get_row()'

  • 'get_icon()', and

  • 'get_attrs()'

  • If you want to show actions Ins/Delete/Edit/Refresh in a popup-menu you should set new bits 'CH_CAN_...' in the 'flags' member.

  • The header line is stored in a new 'header' member.

  • All indexes are now 0-based. You can use new constant 'NO_SELECTION' for non-existing rows.

  • The default value is not stored in the 'chooser_t' structure now and it is passed directly to the 'choose()' function.

  • You can prepare a specialized version of the 'choose()' method that takes a special default value (e.g. an effective address). For this you should implement a new 'get_item_index()' method.

  • The 'update()' callback has been renamed to 'refresh()' and it returns the cursor position after refresh. If the data has not changed this callback should return a 'NOTHING_CHANGED' hint.

  • The returned value of the 'ins()', 'del()', 'edit()' and 'exit()' callbacks are the same as for 'refresh()' callback. E.g. the 'ins()' callback may return the cursor position of the newly inserted item. Or the 'del()' callback may return 'NOTHING_CHANGED' if it asked the user about the removal and he refused.

  • The 'initializer()' callback has been renamed to 'init()'. Its use allows you to prepare data when it is really needed (i.e., "lazy" populating).

  • The 'destroyer()' callback has been renamed to 'closed()' and it is called when the chooser window is about to close. To clean up the chooser data you should use the destructor.

  • The 'CH_MULTI' flag has been removed altogether. If you want to create a chooser with multiple selection, you should derive your class from 'chooser_multi_t'.

  • While callbacks for the 'chooser_t' class would receive and return a single value specifying the currently-selected row, callbacks of the 'chooser_multi_t' class will receive a vector of such values instead.

  • In a similar fashion, instead of using the 'NO_SELECTION' constant, 'chooser_multi_t' will use an empty vector.

  • In contrast to IDA 6.95, the selected items are now all processed at once, in one call to the 'ins()', 'del()', 'edit()' and 'exit()' callbacks (this greatly simplified implementing them.)

Changes per file in the SDK

This section describes in detail the changes to the APIs for each file in the SDK.

auto.hpp

NOTE: global variables 'auto_state', 'auto_display', and 'autoEnabled' have been removed.

  • [1] output argument moved to beginning of argument list

original name
new name
[1]
Notes

autoGetName

<removed>

autoStep

<removed>

<added>

auto_apply_tail

<added>

auto_recreate_insn

<added>

enable_auto

to be used instead of 'autoEnabled'

<added>

get_auto_display

to be used instead of 'auto_display'

<added>

get_auto_state

to be used instead of 'auto_state'

<added>

is_auto_enabled

to be used instead of 'autoEnabled'

<added>

set_auto_state

to be used instead of 'auto_state'

analyze_area

plan_and_wait

added 'final_pass' argument (true for analyze_area behaviour)

autoCancel

auto_cancel

autoIsOk

auto_is_ok

autoMark

auto_mark

autoUnmark

auto_unmark

autoWait

auto_wait

auto_get

*

noUsed

plan_ea

(ea_t ea) variant

noUsed

plan_range

(ea_t sEA, ea_t eEA) variant

setStat

set_ida_state

showAddr

show_addr

showAuto

show_auto

bitrange.hpp

original name
Notes

bitrange_t::extract

argument type: 'int' changed to 'size_t'

bitrange_t::inject

argument type: 'int' changed to 'size_t'

bytes.hpp

NOTE: The misleading term "ASCII string" has been replaced by "string literal" (strlit).

  • [1] output argument moved to beginning of argument list

    • q: argument is a qstring

  • [2] output buffer converted to qstring

original name
new name
[1]
[2]
Notes

clrFlbits

<removed>

do3byte

<removed>

doASCI

<removed>

doVar

<removed>

do_unknown

<removed>

use 'del_items' instead

do_unknown_range

<removed>

use 'del_items' instead

f_is3byte

<removed>

getRadixEA

<removed>

get_3byte

<removed>

get_many_bytes

<removed>

use 'get_bytes' instead

get_many_bytes_ex

<removed>

use 'get_bytes' instead

is3byte

<removed>

isVar

<removed>

noImmd

<removed>

setFlags

<removed>

setFlbits

<removed>

tribyteflag

<removed>

<added>

add_mapping

<added>

attach_custom_data_format

<added>

del_items

<added>

del_mapping

<added>

detach_custom_data_format

<added>

get_bytes

<added>

get_first_hidden_range

<added>

get_last_hidden_range

<added>

get_mapping

<added>

get_mappings_qty

<added>

is_attached_custom_data_format

<added>

revert_byte

<added>

update_hidden_range

<added>

use_mapping

add_hidden_area

add_hidden_range

alignflag

align_flag

asciflag

strlit_flag

binflag

bin_flag

byteflag

byte_flag

charflag

char_flag

chunksize

chunk_size

chunkstart

chunk_start

codeflag

code_flag

custflag

cust_flag

custfmtflag

custfmt_flag

decflag

dec_flag

delValue

del_value

del_hidden_area

del_hidden_range

do16bit

create_16bit_data

do32bit

create_32bit_data

doAlign

create_align

doByte

create_byte

doCustomData

create_custdata

doDouble

create_double

doDwrd

create_dword