Back

IDA 9.3: Practical Improvements to the Type System

IDA 9.3: Practical Improvements to the Type System

The soon to be released, IDA 9.3 tightens up the type system in a few practical ways: Objective-C headers can now be parsed directly, exported headers are easier to read, and type information can make a full round trip between headers and the database without losing structure.

 

Objective-C parser (Clang-based)

IDA 9.3 adds a Clang-based Objective-C parser that understands @interface declarations, methods, categories, protocols, and @property definitions, and stores them as real types in the database. Objective-C interfaces are represented as __objc_interface __cppobj types with proper inheritance, so ivars and superclass layouts are preserved instead of being approximated or flattened.

 

Method signatures are emitted as named types using the familiar -[Class selector:] and +[Class selector:] forms, including automatically generated property accessors. This makes selectors readable, searchable, and reusable as actual type information rather than opaque strings.

Fixed structs round-trip cleanly

This release introduces the __fixed(size) and __at(offset[, bit]) annotations to represent structures with explicit layouts. These annotations preserve exact sizes, offsets, and bitfields when exporting headers, and can be parsed back into IDA without losing layout information.

This makes it possible to round-trip types such as stack frames, ABI-defined records, or packed structures without manual fixes after re-import.

Cleaner header exports

Header exports are easier to work with thanks to the new “Ignore IDA anonymous types” option. When enabled, anonymous structs and unions are inlined directly into their parent types instead of being emitted as hash-named intermediates.

The result is headers that are more readable, less IDA-specific, and better suited for reuse in external tools or for re-importing later without unnecessary noise.

tilib comes equipped with C++ and Objective-C

tilib can now parse C++ and Objective-C headers directly using the built-in Clang parser, so you don’t need to run idaclang just to build a TIL. For new workflows, using tilib directly is recommended. It uses the same parser as the type editor, and will continue to receive new language and type-system features going forward.

Why it matters and What's happening behind the scenes

In practice, this means Objective-C headers can live in Local Types, fixed-layout structs no longer break when round-tripped through C headers, and exported types don’t need manual cleanup before being reused elsewhere.

Objective-C parsing has existed in IDA for a long time through IDAClang, primarily as an import mechanism. In IDA 9.3, the new Clang-based type parser reaches feature parity with IDAClang while making the same functionality available directly in the type editor.

One of the main challenges has been integrating Clang’s richer type information without breaking compatibility with existing IDA parsers and type syntax. The new parser is designed to coexist with those systems, and it will continue to be extended as more language features are brought over.

Where you'll find it in 9.3

Clang parser (GUI)

  1. Options -> Compiler
  2. Set “Source parser” to clang
  3. Parser specific options: check Objective-C or Objective-C++
  4. Add SDK include paths in the “Include directories”.
  5. Local Types -> Add type… (Ins)
  6. Paste your Objective-C code in the type editor

Clang parser (IDAPython)

The same workflow can be driven programmatically using IDAPython:

 

import ida_srclang

import idaapi

 

ida_srclang.set_parser_argv(

    "clang",

    "-x objective-c -target arm64-apple-darwin -framework Foundation -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -ILibrary/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include"

)

idaapi.parse_decls(

    None,

    "/path/to/header.h",

    None,

    idaapi.HTI_FIL | idaapi.HTI_NWR | idaapi.HTI_LOWER | idaapi.HTI_NDC

)

 

The main requirement for successful Objective-C parsing is having the correct headers available to Clang. On macOS, this usually just means pointing the parser at an installed Xcode SDK, since the Objective-C runtime and Foundation headers are already present on the system. In most cases, adding the SDK’s usr/include path is sufficient.

On Linux, the situation is different: Objective-C headers are typically not installed by default. You’ll need to install a runtime and headers yourself. For example, projects based on GNUstep can provide compatible headers (libobjc, libs-base), which can then be added to the Clang include paths used by IDA.

As long as Clang can see a coherent set of Objective-C runtime and framework headers, IDA’s type parser will accept and import the declarations.

Fixed-struct round-trip (create / export / import)

Fixed-layout structures can be created or edited directly in Local Types using the C-syntax editor and the new layout annotations:

 

struct __fixed(0x48) x64_prologue_frame

{

  __at(0x0000) void *return_addr;

  __at(0x0008) void *saved_rbp;

  __at(0x0010) void *saved_rbx;

  __at(0x0038) int local_var1;

  __at(0x003C) int local_var2;

};

 

  • Export: Use File → Produce file → Create C header file…. The generated header will preserve __fixed() and __at() annotations so that layout information is not lost.
  • Import / round-trip: You can give the exported header to a colleague and re-import it in another IDB via File → Load file → Parse C header file…, or programmatically:

idaapi.parse_decls(None, "/path/to/exported.h", None, idaapi.HTI_FIL)

 

You will find your types imported to Local Types, exactly as they were in the original IDB.

Ignore anonymous types (clean header export)

Consider the following type:

struct with_inline_struct_t

{

  struct

  {

    int a;

  } b;

};

 

Parsing it produces hash-named intermediate types:

To avoid such types in exported headers:

GUI

  • Open File → Produce file → Create C header file…
  • Enable Ignore IDA anonymous types

The exported header file will contain the original type, which is fully re-parsable:

/* 35 */

struct with_inline_struct_t

{

  struct

  {

    int a;

  } b;

};

 

IDAPython

import ida_typeinf

 

class def_sink(ida_typeinf.text_sink_t):

    def __init__(self):

        ida_typeinf.text_sink_t.__init__(self)

    def _print(self, defstr):

        print(defstr)

        return 0

       

ida_typeinf.print_decls(def_sink(), None, [], ida_typeinf.PDF_NO_ANON_NAME)

 

This inlines anonymous structs and unions directly into their parent types, producing headers that are easier to read and more suitable for reuse in other tools.

tilib

Existing idaclang-based workflows will continue to work, but tilib provides a more direct path that matches how types are parsed and represented inside IDA itself. For consistency and long-term maintainability, new scripts and automation are expected to use tilib.

 

Examples

C++ headers

tilib -ch/path/to/header.hpp -TC -P -I/path/to/includes out.til

  • -TC enables the Clang parser
  • -P enables C++ mode
  • -I adds include directories

Objective-C headers

tilib -ch/path/to/header.h -TC -CT-xobjective-c -I/path/to/SDK/usr/include out.til

  • -CT-xobjective-c passes the language to Clang

Objective-C++ headers

tilib -ch/path/to/header.mm -TC -CT-xobjective-c++ -I/path/to/SDK/usr/include out.til

 

Wrapping it Up

Together, these changes make the type system in IDA 9.3 more reliable in your daily workflows, particularly for Objective-C and fixed-layout types. If you have real-world headers that still don’t round-trip cleanly, we’d love to hear about them.